13a2a065fSHong Zhang #include <petsc/private/tsimpl.h> /*I "petscts.h" I*/
23a2a065fSHong Zhang #include <petscdm.h>
33a2a065fSHong Zhang #include <../src/ts/impls/arkimex/arkimex.h>
43a2a065fSHong Zhang #include <../src/ts/impls/arkimex/fsarkimex.h>
53a2a065fSHong Zhang
TSARKIMEXSetSplits(TS ts)63a2a065fSHong Zhang static PetscErrorCode TSARKIMEXSetSplits(TS ts)
73a2a065fSHong Zhang {
83a2a065fSHong Zhang TS_ARKIMEX *ark = (TS_ARKIMEX *)ts->data;
93a2a065fSHong Zhang DM dm, subdm, newdm;
103a2a065fSHong Zhang
113a2a065fSHong Zhang PetscFunctionBegin;
123a2a065fSHong Zhang PetscCall(TSRHSSplitGetSubTS(ts, "slow", &ark->subts_slow));
133a2a065fSHong Zhang PetscCall(TSRHSSplitGetSubTS(ts, "fast", &ark->subts_fast));
143a2a065fSHong Zhang /* Only copy the DM */
153a2a065fSHong Zhang PetscCall(TSGetDM(ts, &dm));
163a2a065fSHong Zhang if (ark->subts_slow) {
173a2a065fSHong Zhang PetscCall(DMClone(dm, &newdm));
183a2a065fSHong Zhang PetscCall(TSGetDM(ark->subts_slow, &subdm));
193a2a065fSHong Zhang PetscCall(DMCopyDMTS(subdm, newdm));
203a2a065fSHong Zhang PetscCall(TSSetDM(ark->subts_slow, newdm));
213a2a065fSHong Zhang PetscCall(DMDestroy(&newdm));
223a2a065fSHong Zhang }
233a2a065fSHong Zhang if (ark->subts_fast) {
243a2a065fSHong Zhang PetscCall(DMClone(dm, &newdm));
253a2a065fSHong Zhang PetscCall(TSGetDM(ark->subts_fast, &subdm));
263a2a065fSHong Zhang PetscCall(DMCopyDMTS(subdm, newdm));
273a2a065fSHong Zhang PetscCall(TSSetDM(ark->subts_fast, newdm));
283a2a065fSHong Zhang PetscCall(DMDestroy(&newdm));
293a2a065fSHong Zhang }
303a2a065fSHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
313a2a065fSHong Zhang }
323a2a065fSHong Zhang
SNESTSFormFunction_ARKIMEX_FastSlowSplit(SNES snes,Vec X,Vec F,TS ts)334ce06fd1SHong Zhang static PetscErrorCode SNESTSFormFunction_ARKIMEX_FastSlowSplit(SNES snes, Vec X, Vec F, TS ts)
344ce06fd1SHong Zhang {
354ce06fd1SHong Zhang TS_ARKIMEX *ark = (TS_ARKIMEX *)ts->data;
364ce06fd1SHong Zhang DM dm, dmsave;
374ce06fd1SHong Zhang Vec Z = ark->Z, Ydot = ark->Ydot, Y = ark->Y_snes;
384ce06fd1SHong Zhang
394ce06fd1SHong Zhang PetscFunctionBegin;
404ce06fd1SHong Zhang PetscCall(SNESGetDM(snes, &dm));
414ce06fd1SHong Zhang dmsave = ts->dm;
424ce06fd1SHong Zhang ts->dm = dm; // Use the SNES DM to compute IFunction
434ce06fd1SHong Zhang
444ce06fd1SHong Zhang PetscReal shift = ark->scoeff / ts->time_step;
454ce06fd1SHong Zhang PetscCall(VecAXPBYPCZ(Ydot, -shift, shift, 0, Z, X)); /* Ydot = shift*(X-Z) */
464ce06fd1SHong Zhang if (ark->is_slow) PetscCall(VecISCopy(Y, ark->is_fast, SCATTER_FORWARD, X));
474ce06fd1SHong Zhang else Y = Z;
484ce06fd1SHong Zhang PetscCall(TSComputeIFunction(ark->subts_fast, ark->stage_time, Y, Ydot, F, ark->imex));
494ce06fd1SHong Zhang
504ce06fd1SHong Zhang ts->dm = dmsave;
514ce06fd1SHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
524ce06fd1SHong Zhang }
534ce06fd1SHong Zhang
SNESTSFormJacobian_ARKIMEX_FastSlowSplit(SNES snes,Vec X,Mat A,Mat B,TS ts)544ce06fd1SHong Zhang static PetscErrorCode SNESTSFormJacobian_ARKIMEX_FastSlowSplit(SNES snes, Vec X, Mat A, Mat B, TS ts)
554ce06fd1SHong Zhang {
564ce06fd1SHong Zhang TS_ARKIMEX *ark = (TS_ARKIMEX *)ts->data;
574ce06fd1SHong Zhang DM dm, dmsave;
584ce06fd1SHong Zhang Vec Z = ark->Z, Ydot = ark->Ydot, Y = ark->Y_snes;
594ce06fd1SHong Zhang PetscReal shift;
604ce06fd1SHong Zhang
614ce06fd1SHong Zhang PetscFunctionBegin;
624ce06fd1SHong Zhang PetscCall(SNESGetDM(snes, &dm));
634ce06fd1SHong Zhang dmsave = ts->dm;
644ce06fd1SHong Zhang ts->dm = dm;
654ce06fd1SHong Zhang
664ce06fd1SHong Zhang shift = ark->scoeff / ts->time_step;
674ce06fd1SHong Zhang if (ark->is_slow) PetscCall(VecISCopy(Y, ark->is_fast, SCATTER_FORWARD, X));
684ce06fd1SHong Zhang else Y = Z;
694ce06fd1SHong Zhang PetscCall(TSComputeIJacobian(ark->subts_fast, ark->stage_time, Y, Ydot, shift, A, B, ark->imex));
704ce06fd1SHong Zhang
714ce06fd1SHong Zhang ts->dm = dmsave;
724ce06fd1SHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
734ce06fd1SHong Zhang }
744ce06fd1SHong Zhang
TSExtrapolate_ARKIMEX_FastSlowSplit(TS ts,PetscReal c,Vec X)753a2a065fSHong Zhang static PetscErrorCode TSExtrapolate_ARKIMEX_FastSlowSplit(TS ts, PetscReal c, Vec X)
763a2a065fSHong Zhang {
773a2a065fSHong Zhang TS_ARKIMEX *ark = (TS_ARKIMEX *)ts->data;
783a2a065fSHong Zhang ARKTableau tab = ark->tableau;
793a2a065fSHong Zhang PetscInt s = tab->s, pinterp = tab->pinterp, i, j;
803a2a065fSHong Zhang PetscReal h, h_prev, t, tt;
813a2a065fSHong Zhang PetscScalar *bt = ark->work, *b = ark->work + s;
823a2a065fSHong Zhang const PetscReal *Bt = tab->binterpt, *B = tab->binterp;
833a2a065fSHong Zhang PetscBool fasthasE;
843a2a065fSHong Zhang
853a2a065fSHong Zhang PetscFunctionBegin;
863a2a065fSHong Zhang PetscCheck(Bt && B, PetscObjectComm((PetscObject)ts), PETSC_ERR_SUP, "TSARKIMEX %s does not have an interpolation formula", ark->tableau->name);
873a2a065fSHong Zhang h = ts->time_step;
883a2a065fSHong Zhang h_prev = ts->ptime - ts->ptime_prev;
893a2a065fSHong Zhang t = 1 + h / h_prev * c;
903a2a065fSHong Zhang for (i = 0; i < s; i++) bt[i] = b[i] = 0;
913a2a065fSHong Zhang for (j = 0, tt = t; j < pinterp; j++, tt *= t) {
923a2a065fSHong Zhang for (i = 0; i < s; i++) {
933a2a065fSHong Zhang bt[i] += h * Bt[i * pinterp + j] * tt;
943a2a065fSHong Zhang b[i] += h * B[i * pinterp + j] * tt;
953a2a065fSHong Zhang }
963a2a065fSHong Zhang }
973a2a065fSHong Zhang PetscCheck(ark->Y_prev, PetscObjectComm((PetscObject)ts), PETSC_ERR_SUP, "Stages from previous step have not been stored");
983a2a065fSHong Zhang PetscCall(VecCopy(ark->Y_prev[0], X));
993a2a065fSHong Zhang PetscCall(VecMAXPY(X, s, bt, ark->YdotI_prev));
1003a2a065fSHong Zhang PetscCall(TSHasRHSFunction(ark->subts_fast, &fasthasE));
1013a2a065fSHong Zhang if (fasthasE) PetscCall(VecMAXPY(X, s, b, ark->YdotRHS_prev));
1023a2a065fSHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
1033a2a065fSHong Zhang }
1043a2a065fSHong Zhang
1053a2a065fSHong Zhang /*
1063a2a065fSHong Zhang The step completion formula is
1073a2a065fSHong Zhang
1083a2a065fSHong Zhang x1 = x0 - h bt^T YdotI + h b^T YdotRHS
1093a2a065fSHong Zhang
1103a2a065fSHong Zhang This function can be called before or after ts->vec_sol has been updated.
1113a2a065fSHong Zhang Suppose we have a completion formula (bt,b) and an embedded formula (bet,be) of different order.
1123a2a065fSHong Zhang We can write
1133a2a065fSHong Zhang
1143a2a065fSHong Zhang x1e = x0 - h bet^T YdotI + h be^T YdotRHS
1153a2a065fSHong Zhang = x1 + h bt^T YdotI - h b^T YdotRHS - h bet^T YdotI + h be^T YdotRHS
1163a2a065fSHong Zhang = x1 - h (bet - bt)^T YdotI + h (be - b)^T YdotRHS
1173a2a065fSHong Zhang
1183a2a065fSHong Zhang so we can evaluate the method with different order even after the step has been optimistically completed.
1193a2a065fSHong Zhang */
TSEvaluateStep_ARKIMEX_FastSlowSplit(TS ts,PetscInt order,Vec X,PetscBool * done)1203a2a065fSHong Zhang static PetscErrorCode TSEvaluateStep_ARKIMEX_FastSlowSplit(TS ts, PetscInt order, Vec X, PetscBool *done)
1213a2a065fSHong Zhang {
1223a2a065fSHong Zhang TS_ARKIMEX *ark = (TS_ARKIMEX *)ts->data;
1233a2a065fSHong Zhang ARKTableau tab = ark->tableau;
1243a2a065fSHong Zhang Vec Xfast, Xslow;
1253a2a065fSHong Zhang PetscScalar *w = ark->work;
1263a2a065fSHong Zhang PetscReal h;
1273a2a065fSHong Zhang PetscInt s = tab->s, j;
1283a2a065fSHong Zhang PetscBool fasthasE;
1293a2a065fSHong Zhang
1303a2a065fSHong Zhang PetscFunctionBegin;
1313a2a065fSHong Zhang switch (ark->status) {
1323a2a065fSHong Zhang case TS_STEP_INCOMPLETE:
1333a2a065fSHong Zhang case TS_STEP_PENDING:
1343a2a065fSHong Zhang h = ts->time_step;
1353a2a065fSHong Zhang break;
1363a2a065fSHong Zhang case TS_STEP_COMPLETE:
1373a2a065fSHong Zhang h = ts->ptime - ts->ptime_prev;
1383a2a065fSHong Zhang break;
1393a2a065fSHong Zhang default:
1403a2a065fSHong Zhang SETERRQ(PetscObjectComm((PetscObject)ts), PETSC_ERR_PLIB, "Invalid TSStepStatus");
1413a2a065fSHong Zhang }
1423a2a065fSHong Zhang if (ark->is_fast) PetscCall(TSHasRHSFunction(ark->subts_fast, &fasthasE));
1433a2a065fSHong Zhang if (order == tab->order) {
1443a2a065fSHong Zhang if (ark->status == TS_STEP_INCOMPLETE) {
1453a2a065fSHong Zhang PetscCall(VecCopy(ts->vec_sol, X));
1463a2a065fSHong Zhang for (j = 0; j < s; j++) w[j] = h * tab->b[j];
1473a2a065fSHong Zhang if (ark->is_slow) {
1483a2a065fSHong Zhang PetscCall(VecGetSubVector(X, ark->is_slow, &Xslow));
1493a2a065fSHong Zhang PetscCall(VecMAXPY(Xslow, s, w, ark->YdotRHS_slow));
1503a2a065fSHong Zhang PetscCall(VecRestoreSubVector(X, ark->is_slow, &Xslow));
1513a2a065fSHong Zhang }
1523a2a065fSHong Zhang if (ark->is_fast) {
1533a2a065fSHong Zhang PetscCall(VecGetSubVector(X, ark->is_fast, &Xfast));
1543a2a065fSHong Zhang if (fasthasE) PetscCall(VecMAXPY(Xfast, s, w, ark->YdotRHS_fast));
1553a2a065fSHong Zhang for (j = 0; j < s; j++) w[j] = h * tab->bt[j];
1563a2a065fSHong Zhang PetscCall(VecMAXPY(Xfast, s, w, ark->YdotI_fast));
1573a2a065fSHong Zhang PetscCall(VecRestoreSubVector(X, ark->is_fast, &Xfast));
1583a2a065fSHong Zhang }
1593a2a065fSHong Zhang } else PetscCall(VecCopy(ts->vec_sol, X));
1603a2a065fSHong Zhang if (done) *done = PETSC_TRUE;
1613a2a065fSHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
1623a2a065fSHong Zhang } else if (order == tab->order - 1) {
1633a2a065fSHong Zhang if (!tab->bembedt) goto unavailable;
1643a2a065fSHong Zhang if (ark->status == TS_STEP_INCOMPLETE) { /* Complete with the embedded method (bet,be) */
1653a2a065fSHong Zhang PetscCall(VecCopy(ts->vec_sol, X));
1663a2a065fSHong Zhang for (j = 0; j < s; j++) w[j] = h * tab->bembed[j];
1673a2a065fSHong Zhang if (ark->is_slow) {
1683a2a065fSHong Zhang PetscCall(VecGetSubVector(X, ark->is_slow, &Xslow));
1693a2a065fSHong Zhang PetscCall(VecMAXPY(Xslow, s, w, ark->YdotRHS_slow));
1703a2a065fSHong Zhang PetscCall(VecRestoreSubVector(X, ark->is_slow, &Xslow));
1713a2a065fSHong Zhang }
1723a2a065fSHong Zhang if (ark->is_fast) {
1733a2a065fSHong Zhang PetscCall(VecGetSubVector(X, ark->is_fast, &Xfast));
1743a2a065fSHong Zhang if (fasthasE) PetscCall(VecMAXPY(Xfast, s, w, ark->YdotRHS_fast));
1753a2a065fSHong Zhang for (j = 0; j < s; j++) w[j] = h * tab->bembedt[j];
1763a2a065fSHong Zhang PetscCall(VecMAXPY(Xfast, s, w, ark->YdotI_fast));
1773a2a065fSHong Zhang PetscCall(VecRestoreSubVector(X, ark->is_fast, &Xfast));
1783a2a065fSHong Zhang }
1793a2a065fSHong Zhang } else { /* Rollback and re-complete using (bet-be,be-b) */
1803a2a065fSHong Zhang PetscCall(VecCopy(ts->vec_sol, X));
1813a2a065fSHong Zhang for (j = 0; j < s; j++) w[j] = h * (tab->bembed[j] - tab->b[j]);
1823a2a065fSHong Zhang if (ark->is_slow) {
1833a2a065fSHong Zhang PetscCall(VecGetSubVector(X, ark->is_slow, &Xslow));
1843a2a065fSHong Zhang PetscCall(VecMAXPY(Xslow, s, w, ark->YdotRHS_slow));
1853a2a065fSHong Zhang PetscCall(VecRestoreSubVector(X, ark->is_slow, &Xslow));
1863a2a065fSHong Zhang }
1873a2a065fSHong Zhang if (ark->is_fast) {
1883a2a065fSHong Zhang PetscCall(VecGetSubVector(X, ark->is_fast, &Xfast));
1893a2a065fSHong Zhang if (fasthasE) PetscCall(VecMAXPY(Xfast, tab->s, w, ark->YdotRHS_fast));
1903a2a065fSHong Zhang for (j = 0; j < s; j++) w[j] = h * (tab->bembedt[j] - tab->bt[j]);
1913a2a065fSHong Zhang PetscCall(VecMAXPY(Xfast, tab->s, w, ark->YdotI_fast));
1923a2a065fSHong Zhang PetscCall(VecRestoreSubVector(X, ark->is_fast, &Xfast));
1933a2a065fSHong Zhang }
1943a2a065fSHong Zhang }
1953a2a065fSHong Zhang if (done) *done = PETSC_TRUE;
1963a2a065fSHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
1973a2a065fSHong Zhang }
1983a2a065fSHong Zhang unavailable:
199966bd95aSPierre Jolivet PetscCheck(done, PetscObjectComm((PetscObject)ts), PETSC_ERR_SUP, "ARKIMEX '%s' of order %" PetscInt_FMT " cannot evaluate step at order %" PetscInt_FMT ". Consider using -ts_adapt_type none or a different method that has an embedded estimate.",
200966bd95aSPierre Jolivet tab->name, tab->order, order);
201966bd95aSPierre Jolivet *done = PETSC_FALSE;
2023a2a065fSHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
2033a2a065fSHong Zhang }
2043a2a065fSHong Zhang
2053a2a065fSHong Zhang /*
2063a2a065fSHong Zhang Additive Runge-Kutta methods for a fast-slow (component-wise partitioned) system in the form
2073a2a065fSHong Zhang Ufdot = Ff(t,Uf,Us)
2083a2a065fSHong Zhang Usdot = Fs(t,Uf,Us)
2093a2a065fSHong Zhang
2103a2a065fSHong Zhang Ys[i] = Us_n + dt \sum_{j=1}^{i-1} a[i][j] Fs(t+c_j*dt,Yf[j],Ys[j])
2113a2a065fSHong Zhang Ys[i] = Us_n + dt \sum_{j=1}^{i-1} a[i][j] Fs(t+c_j*dt,Yf[j],Ys[j])
2123a2a065fSHong Zhang
2133a2a065fSHong Zhang */
TSStep_ARKIMEX_FastSlowSplit(TS ts)2143a2a065fSHong Zhang static PetscErrorCode TSStep_ARKIMEX_FastSlowSplit(TS ts)
2153a2a065fSHong Zhang {
2163a2a065fSHong Zhang TS_ARKIMEX *ark = (TS_ARKIMEX *)ts->data;
2173a2a065fSHong Zhang ARKTableau tab = ark->tableau;
2183a2a065fSHong Zhang const PetscInt s = tab->s;
2193a2a065fSHong Zhang const PetscReal *At = tab->At, *A = tab->A, *ct = tab->ct;
2203a2a065fSHong Zhang PetscScalar *w = ark->work;
2213a2a065fSHong Zhang Vec *Y = ark->Y, Ydot_fast = ark->Ydot, Ydot0_fast = ark->Ydot0, Z = ark->Z, *YdotRHS_fast = ark->YdotRHS_fast, *YdotRHS_slow = ark->YdotRHS_slow, *YdotI_fast = ark->YdotI_fast, Yfast, Yslow, Xfast, Xslow;
2223a2a065fSHong Zhang PetscBool extrapolate = ark->extrapolate;
2233a2a065fSHong Zhang TSAdapt adapt;
2243a2a065fSHong Zhang SNES snes;
2253a2a065fSHong Zhang PetscInt i, j, its, lits;
2263a2a065fSHong Zhang PetscInt rejections = 0;
2273a2a065fSHong Zhang PetscBool fasthasE = PETSC_FALSE, stageok, accept = PETSC_TRUE;
2283a2a065fSHong Zhang PetscReal next_time_step = ts->time_step;
2293a2a065fSHong Zhang
2303a2a065fSHong Zhang PetscFunctionBegin;
2313a2a065fSHong Zhang if (ark->is_fast) PetscCall(TSHasRHSFunction(ark->subts_fast, &fasthasE));
2323a2a065fSHong Zhang if (ark->extrapolate && !ark->Y_prev) {
2333a2a065fSHong Zhang PetscCall(VecGetSubVector(ts->vec_sol, ark->is_fast, &Xfast));
2343a2a065fSHong Zhang PetscCall(VecDuplicateVecs(Xfast, tab->s, &ark->Y_prev));
2353a2a065fSHong Zhang PetscCall(VecDuplicateVecs(Xfast, tab->s, &ark->YdotI_prev));
2363a2a065fSHong Zhang if (fasthasE) PetscCall(VecDuplicateVecs(Xfast, tab->s, &ark->YdotRHS_prev));
2373a2a065fSHong Zhang PetscCall(VecRestoreSubVector(ts->vec_sol, ark->is_fast, &Xfast));
2383a2a065fSHong Zhang PetscCall(VecGetSubVector(ts->vec_sol, ark->is_slow, &Xslow));
2393a2a065fSHong Zhang PetscCall(VecRestoreSubVector(ts->vec_sol, ark->is_fast, &Xslow));
2403a2a065fSHong Zhang }
2413a2a065fSHong Zhang
2423a2a065fSHong Zhang if (!ts->steprollback) {
2433a2a065fSHong Zhang if (ts->equation_type >= TS_EQ_IMPLICIT) { /* Save the initial slope for the next step */
2443a2a065fSHong Zhang PetscCall(VecCopy(YdotI_fast[s - 1], Ydot0_fast));
2453a2a065fSHong Zhang }
2463a2a065fSHong Zhang if (ark->extrapolate && !ts->steprestart) { /* Save the Y, YdotI, YdotRHS for extrapolation initial guess */
2473a2a065fSHong Zhang for (i = 0; i < s; i++) {
2483a2a065fSHong Zhang PetscCall(VecISCopy(Y[i], ark->is_fast, SCATTER_REVERSE, ark->Y_prev[i]));
2493a2a065fSHong Zhang PetscCall(VecCopy(YdotI_fast[i], ark->YdotI_prev[i]));
2503a2a065fSHong Zhang if (fasthasE) PetscCall(VecCopy(YdotRHS_fast[i], ark->YdotRHS_prev[i]));
2513a2a065fSHong Zhang }
2523a2a065fSHong Zhang }
2533a2a065fSHong Zhang }
2543a2a065fSHong Zhang
2553a2a065fSHong Zhang /* For IMEX we compute a step */
2563a2a065fSHong Zhang if (ts->equation_type >= TS_EQ_IMPLICIT && tab->explicit_first_stage && ts->steprestart) {
2573a2a065fSHong Zhang TS ts_start;
2583a2a065fSHong Zhang PetscCall(TSClone(ts, &ts_start));
2593a2a065fSHong Zhang PetscCall(TSSetSolution(ts_start, ts->vec_sol));
2603a2a065fSHong Zhang PetscCall(TSSetTime(ts_start, ts->ptime));
2613a2a065fSHong Zhang PetscCall(TSSetMaxSteps(ts_start, ts->steps + 1));
2623a2a065fSHong Zhang PetscCall(TSSetMaxTime(ts_start, ts->ptime + ts->time_step));
2633a2a065fSHong Zhang PetscCall(TSSetExactFinalTime(ts_start, TS_EXACTFINALTIME_STEPOVER));
2643a2a065fSHong Zhang PetscCall(TSSetTimeStep(ts_start, ts->time_step));
2653a2a065fSHong Zhang PetscCall(TSSetType(ts_start, TSARKIMEX));
2663a2a065fSHong Zhang PetscCall(TSARKIMEXSetFullyImplicit(ts_start, PETSC_TRUE));
2673a2a065fSHong Zhang PetscCall(TSARKIMEXSetType(ts_start, TSARKIMEX1BEE));
2683a2a065fSHong Zhang
2693a2a065fSHong Zhang PetscCall(TSRestartStep(ts_start));
2703a2a065fSHong Zhang PetscCall(TSSolve(ts_start, ts->vec_sol));
2713a2a065fSHong Zhang PetscCall(TSGetTime(ts_start, &ts->ptime));
2723a2a065fSHong Zhang PetscCall(TSGetTimeStep(ts_start, &ts->time_step));
2733a2a065fSHong Zhang
2743a2a065fSHong Zhang { /* Save the initial slope for the next step */
2753a2a065fSHong Zhang TS_ARKIMEX *ark_start = (TS_ARKIMEX *)ts_start->data;
2763a2a065fSHong Zhang PetscCall(VecCopy(ark_start->YdotI[ark_start->tableau->s - 1], Ydot0_fast));
2773a2a065fSHong Zhang }
2783a2a065fSHong Zhang ts->steps++;
2793a2a065fSHong Zhang /* Set the correct TS in SNES */
2803a2a065fSHong Zhang /* We'll try to bypass this by changing the method on the fly */
2813a2a065fSHong Zhang {
2824bd3aaa3SHong Zhang PetscCall(TSRHSSplitGetSNES(ts, &snes));
2834bd3aaa3SHong Zhang PetscCall(TSRHSSplitSetSNES(ts, snes));
2843a2a065fSHong Zhang }
2853a2a065fSHong Zhang PetscCall(TSDestroy(&ts_start));
2863a2a065fSHong Zhang }
2873a2a065fSHong Zhang
2883a2a065fSHong Zhang ark->status = TS_STEP_INCOMPLETE;
2893a2a065fSHong Zhang while (!ts->reason && ark->status != TS_STEP_COMPLETE) {
2903a2a065fSHong Zhang PetscReal t = ts->ptime;
2913a2a065fSHong Zhang PetscReal h = ts->time_step;
2923a2a065fSHong Zhang for (i = 0; i < s; i++) {
2933a2a065fSHong Zhang ark->stage_time = t + h * ct[i];
2943a2a065fSHong Zhang PetscCall(TSPreStage(ts, ark->stage_time));
2953a2a065fSHong Zhang PetscCall(VecCopy(ts->vec_sol, Y[i]));
2963a2a065fSHong Zhang /* fast components */
2973a2a065fSHong Zhang if (ark->is_fast) {
2983a2a065fSHong Zhang if (At[i * s + i] == 0) { /* This stage is explicit */
2993a2a065fSHong Zhang PetscCheck(i == 0 || ts->equation_type < TS_EQ_IMPLICIT, PetscObjectComm((PetscObject)ts), PETSC_ERR_SUP, "Explicit stages other than the first one are not supported for implicit problems");
3003a2a065fSHong Zhang PetscCall(VecGetSubVector(Y[i], ark->is_fast, &Yfast));
3013a2a065fSHong Zhang for (j = 0; j < i; j++) w[j] = h * At[i * s + j];
3023a2a065fSHong Zhang PetscCall(VecMAXPY(Yfast, i, w, YdotI_fast));
3033a2a065fSHong Zhang if (fasthasE) {
3043a2a065fSHong Zhang for (j = 0; j < i; j++) w[j] = h * A[i * s + j];
3053a2a065fSHong Zhang PetscCall(VecMAXPY(Yfast, i, w, YdotRHS_fast));
3063a2a065fSHong Zhang }
3073a2a065fSHong Zhang PetscCall(VecRestoreSubVector(Y[i], ark->is_fast, &Yfast));
3083a2a065fSHong Zhang } else {
3093a2a065fSHong Zhang ark->scoeff = 1. / At[i * s + i];
3103a2a065fSHong Zhang /* Ydot = shift*(Y-Z) */
3113a2a065fSHong Zhang PetscCall(VecISCopy(ts->vec_sol, ark->is_fast, SCATTER_REVERSE, Z));
3123a2a065fSHong Zhang for (j = 0; j < i; j++) w[j] = h * At[i * s + j];
3133a2a065fSHong Zhang PetscCall(VecMAXPY(Z, i, w, YdotI_fast));
3143a2a065fSHong Zhang if (fasthasE) {
3153a2a065fSHong Zhang for (j = 0; j < i; j++) w[j] = h * A[i * s + j];
3163a2a065fSHong Zhang PetscCall(VecMAXPY(Z, i, w, YdotRHS_fast));
3173a2a065fSHong Zhang }
3184bd3aaa3SHong Zhang PetscCall(TSRHSSplitGetSNES(ts, &snes));
3193a2a065fSHong Zhang if (ark->is_slow) PetscCall(VecCopy(i > 0 ? Y[i - 1] : ts->vec_sol, ark->Y_snes));
3203a2a065fSHong Zhang else ark->Y_snes = Y[i];
3213a2a065fSHong Zhang PetscCall(VecGetSubVector(Y[i], ark->is_fast, &Yfast));
3223a2a065fSHong Zhang if (extrapolate && !ts->steprestart) {
3233a2a065fSHong Zhang /* Initial guess extrapolated from previous time step stage values */
3243a2a065fSHong Zhang PetscCall(TSExtrapolate_ARKIMEX_FastSlowSplit(ts, ct[i], Yfast));
3253a2a065fSHong Zhang } else {
3263a2a065fSHong Zhang /* Initial guess taken from last stage */
3273a2a065fSHong Zhang PetscCall(VecISCopy(i > 0 ? Y[i - 1] : ts->vec_sol, ark->is_fast, SCATTER_REVERSE, Yfast));
3283a2a065fSHong Zhang }
3293a2a065fSHong Zhang PetscCall(SNESSolve(snes, NULL, Yfast));
3303a2a065fSHong Zhang PetscCall(VecRestoreSubVector(Y[i], ark->is_fast, &Yfast));
3313a2a065fSHong Zhang PetscCall(SNESGetIterationNumber(snes, &its));
3323a2a065fSHong Zhang PetscCall(SNESGetLinearSolveIterations(snes, &lits));
3333a2a065fSHong Zhang ts->snes_its += its;
3343a2a065fSHong Zhang ts->ksp_its += lits;
3353a2a065fSHong Zhang PetscCall(TSGetAdapt(ts, &adapt));
3363a2a065fSHong Zhang PetscCall(TSAdaptCheckStage(adapt, ts, ark->stage_time, Y[i], &stageok));
3373a2a065fSHong Zhang if (!stageok) {
3383a2a065fSHong Zhang /* We are likely rejecting the step because of solver or function domain problems so we should not attempt to
3393a2a065fSHong Zhang * use extrapolation to initialize the solves on the next attempt. */
3403a2a065fSHong Zhang extrapolate = PETSC_FALSE;
3413a2a065fSHong Zhang goto reject_step;
3423a2a065fSHong Zhang }
3433a2a065fSHong Zhang }
3443a2a065fSHong Zhang
3453a2a065fSHong Zhang if (ts->equation_type >= TS_EQ_IMPLICIT) {
3463a2a065fSHong Zhang if (i == 0 && tab->explicit_first_stage) {
3473a2a065fSHong Zhang PetscCheck(tab->stiffly_accurate, PetscObjectComm((PetscObject)ts), PETSC_ERR_SUP, "%s %s is not stiffly accurate and therefore explicit-first stage methods cannot be used if the equation is implicit because the slope cannot be evaluated",
3483a2a065fSHong Zhang ((PetscObject)ts)->type_name, ark->tableau->name);
3493a2a065fSHong Zhang PetscCall(VecCopy(Ydot0_fast, YdotI_fast[0])); /* YdotI_fast = YdotI_fast(tn-1) */
3503a2a065fSHong Zhang } else {
3513a2a065fSHong Zhang PetscCall(VecGetSubVector(Y[i], ark->is_fast, &Yfast));
3523a2a065fSHong Zhang PetscCall(VecAXPBYPCZ(YdotI_fast[i], -ark->scoeff / h, ark->scoeff / h, 0, Z, Yfast)); /* YdotI = shift*(X-Z) */
3533a2a065fSHong Zhang PetscCall(VecRestoreSubVector(Y[i], ark->is_fast, &Yfast));
3543a2a065fSHong Zhang }
3553a2a065fSHong Zhang } else {
3563a2a065fSHong Zhang if (i == 0 && tab->explicit_first_stage) {
3573a2a065fSHong Zhang PetscCall(VecZeroEntries(Ydot_fast));
3583a2a065fSHong Zhang PetscCall(TSComputeIFunction(ark->subts_fast, ark->stage_time, Y[i], Ydot_fast, YdotI_fast[i], ark->imex)); /* YdotI = -G(t,Y,0) */
3593a2a065fSHong Zhang PetscCall(VecScale(YdotI_fast[i], -1.0));
3603a2a065fSHong Zhang } else {
3613a2a065fSHong Zhang PetscCall(VecGetSubVector(Y[i], ark->is_fast, &Yfast));
3623a2a065fSHong Zhang PetscCall(VecAXPBYPCZ(YdotI_fast[i], -ark->scoeff / h, ark->scoeff / h, 0, Z, Yfast)); /* YdotI = shift*(X-Z) */
3633a2a065fSHong Zhang PetscCall(VecRestoreSubVector(Y[i], ark->is_fast, &Yfast));
3643a2a065fSHong Zhang }
3653a2a065fSHong Zhang if (fasthasE) PetscCall(TSComputeRHSFunction(ark->subts_fast, ark->stage_time, Y[i], YdotRHS_fast[i]));
3663a2a065fSHong Zhang }
3673a2a065fSHong Zhang }
3683a2a065fSHong Zhang /* slow components */
3693a2a065fSHong Zhang if (ark->is_slow) {
3703a2a065fSHong Zhang for (j = 0; j < i; j++) w[j] = h * A[i * s + j];
3713a2a065fSHong Zhang PetscCall(VecGetSubVector(Y[i], ark->is_slow, &Yslow));
3723a2a065fSHong Zhang PetscCall(VecMAXPY(Yslow, i, w, YdotRHS_slow));
3733a2a065fSHong Zhang PetscCall(VecRestoreSubVector(Y[i], ark->is_slow, &Yslow));
3743a2a065fSHong Zhang PetscCall(TSComputeRHSFunction(ark->subts_slow, ark->stage_time, Y[i], YdotRHS_slow[i]));
3753a2a065fSHong Zhang }
3763a2a065fSHong Zhang PetscCall(TSPostStage(ts, ark->stage_time, i, Y));
3773a2a065fSHong Zhang }
3783a2a065fSHong Zhang ark->status = TS_STEP_INCOMPLETE;
3793a2a065fSHong Zhang PetscCall(TSEvaluateStep_ARKIMEX_FastSlowSplit(ts, tab->order, ts->vec_sol, NULL));
3803a2a065fSHong Zhang ark->status = TS_STEP_PENDING;
3813a2a065fSHong Zhang PetscCall(TSGetAdapt(ts, &adapt));
3823a2a065fSHong Zhang PetscCall(TSAdaptCandidatesClear(adapt));
3833a2a065fSHong Zhang PetscCall(TSAdaptCandidateAdd(adapt, tab->name, tab->order, 1, tab->ccfl, (PetscReal)tab->s, PETSC_TRUE));
3843a2a065fSHong Zhang PetscCall(TSAdaptChoose(adapt, ts, ts->time_step, NULL, &next_time_step, &accept));
3853a2a065fSHong Zhang ark->status = accept ? TS_STEP_COMPLETE : TS_STEP_INCOMPLETE;
3863a2a065fSHong Zhang if (!accept) { /* Roll back the current step */
3873a2a065fSHong Zhang PetscCall(VecCopy(ts->vec_sol0, ts->vec_sol));
3883a2a065fSHong Zhang ts->time_step = next_time_step;
3893a2a065fSHong Zhang goto reject_step;
3903a2a065fSHong Zhang }
3913a2a065fSHong Zhang
3923a2a065fSHong Zhang ts->ptime += ts->time_step;
3933a2a065fSHong Zhang ts->time_step = next_time_step;
3943a2a065fSHong Zhang break;
3953a2a065fSHong Zhang
3963a2a065fSHong Zhang reject_step:
3973a2a065fSHong Zhang ts->reject++;
3983a2a065fSHong Zhang accept = PETSC_FALSE;
3993a2a065fSHong Zhang if (!ts->reason && ++rejections > ts->max_reject && ts->max_reject >= 0) {
4003a2a065fSHong Zhang ts->reason = TS_DIVERGED_STEP_REJECTED;
4013a2a065fSHong Zhang PetscCall(PetscInfo(ts, "Step=%" PetscInt_FMT ", step rejections %" PetscInt_FMT " greater than current TS allowed, stopping solve\n", ts->steps, rejections));
4023a2a065fSHong Zhang }
4033a2a065fSHong Zhang }
4043a2a065fSHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
4053a2a065fSHong Zhang }
4063a2a065fSHong Zhang
TSSetUp_ARKIMEX_FastSlowSplit(TS ts)4073a2a065fSHong Zhang static PetscErrorCode TSSetUp_ARKIMEX_FastSlowSplit(TS ts)
4083a2a065fSHong Zhang {
4093a2a065fSHong Zhang TS_ARKIMEX *ark = (TS_ARKIMEX *)ts->data;
4103a2a065fSHong Zhang ARKTableau tab = ark->tableau;
4113a2a065fSHong Zhang Vec Xfast, Xslow;
4123a2a065fSHong Zhang
4133a2a065fSHong Zhang PetscFunctionBegin;
4143a2a065fSHong Zhang PetscCall(PetscMalloc1(2 * tab->s, &ark->work));
4153a2a065fSHong Zhang PetscCall(VecDuplicateVecs(ts->vec_sol, tab->s, &ark->Y));
4163a2a065fSHong Zhang PetscCall(TSRHSSplitGetIS(ts, "slow", &ark->is_slow));
4173a2a065fSHong Zhang PetscCall(TSRHSSplitGetIS(ts, "fast", &ark->is_fast));
4183a2a065fSHong Zhang PetscCheck(ark->is_slow || ark->is_fast, PetscObjectComm((PetscObject)ts), PETSC_ERR_USER, "Must set up RHSSplits with TSRHSSplitSetIS() using split names 'slow' or 'fast' or both in order to use -ts_arkimex_fastslow true");
4193a2a065fSHong Zhang /* The following vectors need to be resized */
4203a2a065fSHong Zhang PetscCall(VecDestroy(&ark->Ydot));
4213a2a065fSHong Zhang PetscCall(VecDestroy(&ark->Ydot0));
4223a2a065fSHong Zhang PetscCall(VecDestroy(&ark->Z));
4233a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->YdotI_fast));
4243a2a065fSHong Zhang if (ark->extrapolate && ark->is_slow) { // need to resize these vectors if the fast subvectors is smaller than their original counterparts (which means IS)
4253a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->Y_prev));
4263a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->YdotI_prev));
4273a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->YdotRHS_prev));
4283a2a065fSHong Zhang }
4293a2a065fSHong Zhang /* Allocate working vectors */
4303a2a065fSHong Zhang if (ark->is_fast && ark->is_slow) PetscCall(VecDuplicate(ts->vec_sol, &ark->Y_snes)); // need an additional work vector for SNES
4313a2a065fSHong Zhang if (ark->is_fast) {
4323a2a065fSHong Zhang PetscCall(VecGetSubVector(ts->vec_sol, ark->is_fast, &Xfast));
4333a2a065fSHong Zhang PetscCall(VecDuplicateVecs(Xfast, tab->s, &ark->YdotRHS_fast));
4343a2a065fSHong Zhang PetscCall(VecDuplicateVecs(Xfast, tab->s, &ark->YdotI_fast));
4353a2a065fSHong Zhang PetscCall(VecDuplicate(Xfast, &ark->Ydot));
4363a2a065fSHong Zhang PetscCall(VecDuplicate(Xfast, &ark->Ydot0));
4373a2a065fSHong Zhang PetscCall(VecDuplicate(Xfast, &ark->Z));
4383a2a065fSHong Zhang if (ark->extrapolate) {
4393a2a065fSHong Zhang PetscCall(VecDuplicateVecs(Xfast, tab->s, &ark->Y_prev));
4403a2a065fSHong Zhang PetscCall(VecDuplicateVecs(Xfast, tab->s, &ark->YdotI_prev));
4413a2a065fSHong Zhang PetscCall(VecDuplicateVecs(Xfast, tab->s, &ark->YdotRHS_prev));
4423a2a065fSHong Zhang }
4433a2a065fSHong Zhang PetscCall(VecRestoreSubVector(ts->vec_sol, ark->is_fast, &Xfast));
4443a2a065fSHong Zhang }
4453a2a065fSHong Zhang if (ark->is_slow) {
4463a2a065fSHong Zhang PetscCall(VecGetSubVector(ts->vec_sol, ark->is_slow, &Xslow));
4473a2a065fSHong Zhang PetscCall(VecDuplicateVecs(Xslow, tab->s, &ark->YdotRHS_slow));
4483a2a065fSHong Zhang PetscCall(VecRestoreSubVector(ts->vec_sol, ark->is_slow, &Xslow));
4493a2a065fSHong Zhang }
4503a2a065fSHong Zhang ts->ops->step = TSStep_ARKIMEX_FastSlowSplit;
4513a2a065fSHong Zhang ts->ops->evaluatestep = TSEvaluateStep_ARKIMEX_FastSlowSplit;
4523a2a065fSHong Zhang PetscCall(TSARKIMEXSetSplits(ts));
4533a2a065fSHong Zhang if (ark->subts_fast) { // subts SNESJacobian is set when users set the subts Jacobian, but the main ts SNESJacobian needs to be set too
4543a2a065fSHong Zhang SNES snes, snes_fast;
455*4bb18b8cSHong Zhang Mat Amat, Pmat;
4563a2a065fSHong Zhang PetscErrorCode (*func)(SNES, Vec, Mat, Mat, void *);
4574bd3aaa3SHong Zhang PetscCall(TSRHSSplitGetSNES(ts, &snes));
4583a2a065fSHong Zhang PetscCall(TSGetSNES(ark->subts_fast, &snes_fast));
459*4bb18b8cSHong Zhang PetscCall(SNESGetJacobian(snes_fast, &Amat, &Pmat, &func, NULL));
460*4bb18b8cSHong Zhang if (func == SNESTSFormJacobian) PetscCall(SNESSetJacobian(snes, Amat, Pmat, SNESTSFormJacobian, ts));
4614ce06fd1SHong Zhang ts->ops->snesfunction = SNESTSFormFunction_ARKIMEX_FastSlowSplit;
4624ce06fd1SHong Zhang ts->ops->snesjacobian = SNESTSFormJacobian_ARKIMEX_FastSlowSplit;
4633a2a065fSHong Zhang }
4643a2a065fSHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
4653a2a065fSHong Zhang }
4663a2a065fSHong Zhang
TSReset_ARKIMEX_FastSlowSplit(TS ts)4673a2a065fSHong Zhang static PetscErrorCode TSReset_ARKIMEX_FastSlowSplit(TS ts)
4683a2a065fSHong Zhang {
4693a2a065fSHong Zhang TS_ARKIMEX *ark = (TS_ARKIMEX *)ts->data;
4703a2a065fSHong Zhang ARKTableau tab = ark->tableau;
4713a2a065fSHong Zhang
4723a2a065fSHong Zhang PetscFunctionBegin;
4733a2a065fSHong Zhang if (tab) {
4743a2a065fSHong Zhang PetscCall(PetscFree(ark->work));
4753a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->Y));
4763a2a065fSHong Zhang if (ark->is_fast && ark->is_slow) PetscCall(VecDestroy(&ark->Y_snes));
4773a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->YdotRHS_slow));
4783a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->YdotRHS_fast));
4793a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->YdotI_fast));
4803a2a065fSHong Zhang PetscCall(VecDestroy(&ark->Ydot));
4813a2a065fSHong Zhang PetscCall(VecDestroy(&ark->Ydot0));
4823a2a065fSHong Zhang PetscCall(VecDestroy(&ark->Z));
4833a2a065fSHong Zhang if (ark->extrapolate) {
4843a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->Y_prev));
4853a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->YdotI_prev));
4863a2a065fSHong Zhang PetscCall(VecDestroyVecs(tab->s, &ark->YdotRHS_prev));
4873a2a065fSHong Zhang }
4883a2a065fSHong Zhang }
4893a2a065fSHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
4903a2a065fSHong Zhang }
4913a2a065fSHong Zhang
TSARKIMEXSetFastSlowSplit_ARKIMEX(TS ts,PetscBool fastslowsplit)4923a2a065fSHong Zhang PetscErrorCode TSARKIMEXSetFastSlowSplit_ARKIMEX(TS ts, PetscBool fastslowsplit)
4933a2a065fSHong Zhang {
4943a2a065fSHong Zhang TS_ARKIMEX *ark = (TS_ARKIMEX *)ts->data;
4953a2a065fSHong Zhang
4963a2a065fSHong Zhang PetscFunctionBegin;
4973a2a065fSHong Zhang PetscValidHeaderSpecific(ts, TS_CLASSID, 1);
4983a2a065fSHong Zhang ark->fastslowsplit = fastslowsplit;
4993a2a065fSHong Zhang if (fastslowsplit) {
5003a2a065fSHong Zhang PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSSetUp_ARKIMEX_FastSlowSplit_C", TSSetUp_ARKIMEX_FastSlowSplit));
5013a2a065fSHong Zhang PetscCall(PetscObjectComposeFunction((PetscObject)ts, "TSReset_ARKIMEX_FastSlowSplit_C", TSReset_ARKIMEX_FastSlowSplit));
5023a2a065fSHong Zhang }
5033a2a065fSHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
5043a2a065fSHong Zhang }
5053a2a065fSHong Zhang
TSARKIMEXGetFastSlowSplit_ARKIMEX(TS ts,PetscBool * fastslowsplit)5063a2a065fSHong Zhang PetscErrorCode TSARKIMEXGetFastSlowSplit_ARKIMEX(TS ts, PetscBool *fastslowsplit)
5073a2a065fSHong Zhang {
5083a2a065fSHong Zhang TS_ARKIMEX *ark = (TS_ARKIMEX *)ts->data;
5093a2a065fSHong Zhang
5103a2a065fSHong Zhang PetscFunctionBegin;
5113a2a065fSHong Zhang PetscValidHeaderSpecific(ts, TS_CLASSID, 1);
5123a2a065fSHong Zhang *fastslowsplit = ark->fastslowsplit;
5133a2a065fSHong Zhang PetscFunctionReturn(PETSC_SUCCESS);
5143a2a065fSHong Zhang }
515