1ca4445c7SIlya Fursov #include <petscts.h> 2ca4445c7SIlya Fursov #include <stdio.h> 3ca4445c7SIlya Fursov 4*fe4ad979SIlya Fursov #define NEW_VERSION // Applicable for the new features; avoid this for the older PETSc versions (without TSSetPostEventStep()) 5ca4445c7SIlya Fursov 6ca4445c7SIlya Fursov static char help[] = "Simple linear problem with events\n" 7ca4445c7SIlya Fursov "x_dot = 0.2*y\n" 8ca4445c7SIlya Fursov "y_dot = -0.2*x\n" 9ca4445c7SIlya Fursov "Using one event function = sin(pi*t), zeros = 1,...,10\n" 10ca4445c7SIlya Fursov "Options:\n" 11ca4445c7SIlya Fursov "-dir d : zero-crossing direction for events\n" 12ca4445c7SIlya Fursov "-flg : additional output in Postevent\n" 13*fe4ad979SIlya Fursov "-errtol e : error tolerance, for printing 'pass/fail' for located events (1e-5 by default)\n" 14ca4445c7SIlya Fursov "-restart : flag for TSRestartStep() in PostEvent\n" 15*fe4ad979SIlya Fursov "-dtpost x : if x > 0, then on even PostEvent calls 1st-post-event-step = x is set,\n" 16*fe4ad979SIlya Fursov " on odd PostEvent calls 1st-post-event-step = PETSC_DECIDE is set,\n" 17ca4445c7SIlya Fursov " if x == 0, nothing happens\n"; 18ca4445c7SIlya Fursov 19ca4445c7SIlya Fursov #define MAX_NFUNC 100 // max event functions per rank 20ca4445c7SIlya Fursov #define MAX_NEV 5000 // max zero crossings for each rank 21ca4445c7SIlya Fursov 22ca4445c7SIlya Fursov typedef struct { 23ca4445c7SIlya Fursov PetscMPIInt rank, size; 24ca4445c7SIlya Fursov PetscReal pi; 25ca4445c7SIlya Fursov PetscReal fvals[MAX_NFUNC]; // helper array for reporting the residuals 26ca4445c7SIlya Fursov PetscReal evres[MAX_NEV]; // times of found zero-crossings 27*fe4ad979SIlya Fursov PetscReal ref[MAX_NEV]; // reference times of zero-crossings, for checking 28ca4445c7SIlya Fursov PetscInt cnt; // counter 29*fe4ad979SIlya Fursov PetscInt cntref; // actual length of 'ref' on the given rank 30ca4445c7SIlya Fursov PetscBool flg; // flag for additional print in PostEvent 31*fe4ad979SIlya Fursov PetscReal errtol; // error tolerance, for printing 'pass/fail' for located events (1e-5 by default) 32ca4445c7SIlya Fursov PetscBool restart; // flag for TSRestartStep() in PostEvent 33ca4445c7SIlya Fursov PetscReal dtpost; // post-event step 34ca4445c7SIlya Fursov PetscInt postcnt; // counter for PostEvent calls 35ca4445c7SIlya Fursov } AppCtx; 36ca4445c7SIlya Fursov 37ca4445c7SIlya Fursov PetscErrorCode EventFunction(TS ts, PetscReal t, Vec U, PetscReal gval[], void *ctx); 38ca4445c7SIlya Fursov PetscErrorCode Postevent(TS ts, PetscInt nev_zero, PetscInt evs_zero[], PetscReal t, Vec U, PetscBool fwd, void *ctx); 39ca4445c7SIlya Fursov 40ca4445c7SIlya Fursov int main(int argc, char **argv) 41ca4445c7SIlya Fursov { 42ca4445c7SIlya Fursov TS ts; 43ca4445c7SIlya Fursov Mat A; 44ca4445c7SIlya Fursov Vec sol; 45ca4445c7SIlya Fursov PetscInt n, dir0, m = 0; 46ca4445c7SIlya Fursov PetscInt dir[MAX_NFUNC], inds[2]; 47ca4445c7SIlya Fursov PetscBool term[MAX_NFUNC]; 48ca4445c7SIlya Fursov PetscScalar *x, vals[4]; 49ca4445c7SIlya Fursov AppCtx ctx; 50ca4445c7SIlya Fursov 51ca4445c7SIlya Fursov PetscFunctionBeginUser; 52ca4445c7SIlya Fursov PetscCall(PetscInitialize(&argc, &argv, (char *)0, help)); 53ca4445c7SIlya Fursov setbuf(stdout, NULL); 54ca4445c7SIlya Fursov PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &ctx.rank)); 55ca4445c7SIlya Fursov PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &ctx.size)); 56ca4445c7SIlya Fursov ctx.pi = PetscAcosReal(-1.0); 57ca4445c7SIlya Fursov ctx.cnt = 0; 58*fe4ad979SIlya Fursov ctx.cntref = 0; 59ca4445c7SIlya Fursov ctx.flg = PETSC_FALSE; 60*fe4ad979SIlya Fursov ctx.errtol = 1e-5; 61ca4445c7SIlya Fursov ctx.restart = PETSC_FALSE; 62ca4445c7SIlya Fursov ctx.dtpost = 0; 63ca4445c7SIlya Fursov ctx.postcnt = 0; 64ca4445c7SIlya Fursov 65ca4445c7SIlya Fursov // The linear problem has a 2*2 matrix. The matrix is constant 66ca4445c7SIlya Fursov if (ctx.rank == 0) m = 2; 67ca4445c7SIlya Fursov inds[0] = 0; 68ca4445c7SIlya Fursov inds[1] = 1; 69ca4445c7SIlya Fursov vals[0] = 0; 70ca4445c7SIlya Fursov vals[1] = 0.2; 71ca4445c7SIlya Fursov vals[2] = -0.2; 72ca4445c7SIlya Fursov vals[3] = 0; 73ca4445c7SIlya Fursov PetscCall(MatCreateAIJ(PETSC_COMM_WORLD, m, m, PETSC_DETERMINE, PETSC_DETERMINE, 2, NULL, 0, NULL, &A)); 74ca4445c7SIlya Fursov PetscCall(MatSetValues(A, m, inds, m, inds, vals, INSERT_VALUES)); 75ca4445c7SIlya Fursov PetscCall(MatAssemblyBegin(A, MAT_FINAL_ASSEMBLY)); 76ca4445c7SIlya Fursov PetscCall(MatAssemblyEnd(A, MAT_FINAL_ASSEMBLY)); 77ca4445c7SIlya Fursov PetscCall(MatSetOption(A, MAT_NEW_NONZERO_LOCATION_ERR, PETSC_TRUE)); 78ca4445c7SIlya Fursov 79ca4445c7SIlya Fursov PetscCall(MatCreateVecs(A, &sol, NULL)); 80ca4445c7SIlya Fursov PetscCall(VecGetArray(sol, &x)); 81ca4445c7SIlya Fursov if (ctx.rank == 0) { // initial conditions 82ca4445c7SIlya Fursov x[0] = 0; // sin(0) 83ca4445c7SIlya Fursov x[1] = 1; // cos(0) 84ca4445c7SIlya Fursov } 85ca4445c7SIlya Fursov PetscCall(VecRestoreArray(sol, &x)); 86ca4445c7SIlya Fursov 87ca4445c7SIlya Fursov PetscCall(TSCreate(PETSC_COMM_WORLD, &ts)); 88ca4445c7SIlya Fursov PetscCall(TSSetProblemType(ts, TS_LINEAR)); 89ca4445c7SIlya Fursov 90ca4445c7SIlya Fursov PetscCall(TSSetRHSFunction(ts, NULL, TSComputeRHSFunctionLinear, NULL)); 91ca4445c7SIlya Fursov PetscCall(TSSetRHSJacobian(ts, A, A, TSComputeRHSJacobianConstant, NULL)); 92ca4445c7SIlya Fursov 93ca4445c7SIlya Fursov PetscCall(TSSetTimeStep(ts, 0.1)); 94ca4445c7SIlya Fursov PetscCall(TSSetType(ts, TSBEULER)); 95ca4445c7SIlya Fursov PetscCall(TSSetMaxSteps(ts, 10000)); 96ca4445c7SIlya Fursov PetscCall(TSSetMaxTime(ts, 10.0)); 97ca4445c7SIlya Fursov PetscCall(TSSetExactFinalTime(ts, TS_EXACTFINALTIME_MATCHSTEP)); 98ca4445c7SIlya Fursov PetscCall(TSSetFromOptions(ts)); 99ca4445c7SIlya Fursov 100ca4445c7SIlya Fursov // Set the event handling 101ca4445c7SIlya Fursov dir0 = 0; 102ca4445c7SIlya Fursov PetscCall(PetscOptionsGetInt(NULL, NULL, "-dir", &dir0, NULL)); // desired zero-crossing direction 103ca4445c7SIlya Fursov PetscCall(PetscOptionsHasName(NULL, NULL, "-flg", &ctx.flg)); // flag for additional output 104*fe4ad979SIlya Fursov PetscCall(PetscOptionsGetReal(NULL, NULL, "-errtol", &ctx.errtol, NULL)); // error tolerance for located events 105ca4445c7SIlya Fursov PetscCall(PetscOptionsGetBool(NULL, NULL, "-restart", &ctx.restart, NULL)); // flag for TSRestartStep() 106ca4445c7SIlya Fursov PetscCall(PetscOptionsGetReal(NULL, NULL, "-dtpost", &ctx.dtpost, NULL)); // post-event step 107ca4445c7SIlya Fursov 108ca4445c7SIlya Fursov n = 0; // event counter 109ca4445c7SIlya Fursov if (ctx.rank == 0) { // first event -- on rank-0 110ca4445c7SIlya Fursov dir[n] = dir0; 111ca4445c7SIlya Fursov term[n++] = PETSC_FALSE; 112*fe4ad979SIlya Fursov 113*fe4ad979SIlya Fursov for (PetscInt i = 1; i < MAX_NEV; i++) { 114*fe4ad979SIlya Fursov if (i % 2 == 1 && dir0 <= 0) ctx.ref[ctx.cntref++] = i; 115*fe4ad979SIlya Fursov if (i % 2 == 0 && dir0 >= 0) ctx.ref[ctx.cntref++] = i; 116ca4445c7SIlya Fursov } 117*fe4ad979SIlya Fursov } 118*fe4ad979SIlya Fursov if (ctx.cntref > 0) PetscCall(PetscSortReal(ctx.cntref, ctx.ref)); 119ca4445c7SIlya Fursov PetscCall(TSSetEventHandler(ts, n, dir, term, EventFunction, Postevent, &ctx)); 120ca4445c7SIlya Fursov 121ca4445c7SIlya Fursov // Solution 122ca4445c7SIlya Fursov PetscCall(TSSolve(ts, sol)); 123ca4445c7SIlya Fursov 124*fe4ad979SIlya Fursov // The 4 columns printed are: [RANK] [time of event] [error w.r.t. reference] ["pass"/"fail"] 125*fe4ad979SIlya Fursov for (PetscInt j = 0; j < ctx.cnt; j++) { 126*fe4ad979SIlya Fursov PetscReal err = 10.0; 127*fe4ad979SIlya Fursov if (j < ctx.cntref) err = PetscAbsReal(ctx.evres[j] - ctx.ref[j]); 128*fe4ad979SIlya Fursov PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "%d\t%g\t%g\t%s\n", ctx.rank, (double)ctx.evres[j], (double)err, err < ctx.errtol ? "pass" : "fail")); 129*fe4ad979SIlya Fursov } 130ca4445c7SIlya Fursov PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT)); 131ca4445c7SIlya Fursov 132ca4445c7SIlya Fursov PetscCall(MatDestroy(&A)); 133ca4445c7SIlya Fursov PetscCall(TSDestroy(&ts)); 134ca4445c7SIlya Fursov PetscCall(VecDestroy(&sol)); 135ca4445c7SIlya Fursov 136ca4445c7SIlya Fursov PetscCall(PetscFinalize()); 137ca4445c7SIlya Fursov return 0; 138ca4445c7SIlya Fursov } 139ca4445c7SIlya Fursov 140ca4445c7SIlya Fursov /* 141ca4445c7SIlya Fursov User callback for defining the event-functions 142ca4445c7SIlya Fursov */ 143ca4445c7SIlya Fursov PetscErrorCode EventFunction(TS ts, PetscReal t, Vec U, PetscReal gval[], void *ctx) 144ca4445c7SIlya Fursov { 145ca4445c7SIlya Fursov PetscInt n = 0; 146ca4445c7SIlya Fursov AppCtx *Ctx = (AppCtx *)ctx; 147ca4445c7SIlya Fursov 148ca4445c7SIlya Fursov PetscFunctionBeginUser; 149ca4445c7SIlya Fursov // for the test purposes, event-functions are defined based on t 150ca4445c7SIlya Fursov // first event -- on rank-0 151ca4445c7SIlya Fursov if (Ctx->rank == 0) { gval[n++] = PetscSinReal(Ctx->pi * t); } 152ca4445c7SIlya Fursov PetscFunctionReturn(PETSC_SUCCESS); 153ca4445c7SIlya Fursov } 154ca4445c7SIlya Fursov 155ca4445c7SIlya Fursov /* 156ca4445c7SIlya Fursov User callback for the post-event stuff 157ca4445c7SIlya Fursov */ 158ca4445c7SIlya Fursov PetscErrorCode Postevent(TS ts, PetscInt nev_zero, PetscInt evs_zero[], PetscReal t, Vec U, PetscBool fwd, void *ctx) 159ca4445c7SIlya Fursov { 160ca4445c7SIlya Fursov AppCtx *Ctx = (AppCtx *)ctx; 161ca4445c7SIlya Fursov 162ca4445c7SIlya Fursov PetscFunctionBeginUser; 163ca4445c7SIlya Fursov if (Ctx->flg) { 164ca4445c7SIlya Fursov PetscCallBack("EventFunction", EventFunction(ts, t, U, Ctx->fvals, ctx)); 165ca4445c7SIlya Fursov PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "[%d] At t = %20.16g : %" PetscInt_FMT " events triggered, fvalues =", Ctx->rank, (double)t, nev_zero)); 166ca4445c7SIlya Fursov for (PetscInt j = 0; j < nev_zero; j++) PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "\t%g", (double)Ctx->fvals[evs_zero[j]])); 167ca4445c7SIlya Fursov PetscCall(PetscSynchronizedPrintf(PETSC_COMM_WORLD, "\n")); 168ca4445c7SIlya Fursov PetscCall(PetscSynchronizedFlush(PETSC_COMM_WORLD, PETSC_STDOUT)); 169ca4445c7SIlya Fursov } 170ca4445c7SIlya Fursov 171*fe4ad979SIlya Fursov if (Ctx->cnt + nev_zero < MAX_NEV) 172*fe4ad979SIlya Fursov for (PetscInt i = 0; i < nev_zero; i++) Ctx->evres[Ctx->cnt++] = t; // save the repeating zeros separately for easier/unified testing 173ca4445c7SIlya Fursov 174ca4445c7SIlya Fursov #ifdef NEW_VERSION 175ca4445c7SIlya Fursov Ctx->postcnt++; // sync 176ca4445c7SIlya Fursov if (Ctx->dtpost > 0) { 177ca4445c7SIlya Fursov if (Ctx->postcnt % 2 == 0) PetscCall(TSSetPostEventStep(ts, Ctx->dtpost)); 178*fe4ad979SIlya Fursov else PetscCall(TSSetPostEventStep(ts, PETSC_DECIDE)); 179ca4445c7SIlya Fursov } 180ca4445c7SIlya Fursov #endif 181ca4445c7SIlya Fursov 182ca4445c7SIlya Fursov if (Ctx->restart) PetscCall(TSRestartStep(ts)); 183ca4445c7SIlya Fursov PetscFunctionReturn(PETSC_SUCCESS); 184ca4445c7SIlya Fursov } 185ca4445c7SIlya Fursov /*---------------------------------------------------------------------------------------------*/ 186*fe4ad979SIlya Fursov /* 187*fe4ad979SIlya Fursov Note, in the tests below, -ts_event_post_event_step is occasionally set to -1, 188*fe4ad979SIlya Fursov which corresponds to PETSC_DECIDE in the API. It is not a very good practice to 189*fe4ad979SIlya Fursov explicitly specify -1 in this option. Rather, if PETSC_DECIDE behaviour is needed, 190*fe4ad979SIlya Fursov simply remove this option altogether. This will result in using the defaults 191*fe4ad979SIlya Fursov (which is PETSC_DECIDE). 192*fe4ad979SIlya Fursov */ 193ca4445c7SIlya Fursov /*TEST 194ca4445c7SIlya Fursov test: 195ca4445c7SIlya Fursov suffix: 0 196*fe4ad979SIlya Fursov requires: !single 197ca4445c7SIlya Fursov output_file: output/ex1sin_0.out 198ca4445c7SIlya Fursov args: -dir 0 199*fe4ad979SIlya Fursov args: -restart 0 200ca4445c7SIlya Fursov args: -dtpost {{0 0.25}} 201*fe4ad979SIlya Fursov args: -ts_event_post_event_step -1 202ca4445c7SIlya Fursov args: -ts_type {{beuler rk}} 203ca4445c7SIlya Fursov args: -ts_adapt_type {{none basic}} 204*fe4ad979SIlya Fursov nsize: 1 205ca4445c7SIlya Fursov 206ca4445c7SIlya Fursov test: 207ca4445c7SIlya Fursov suffix: p 208*fe4ad979SIlya Fursov requires: !single 209ca4445c7SIlya Fursov output_file: output/ex1sin_p.out 210ca4445c7SIlya Fursov args: -dir 1 211*fe4ad979SIlya Fursov args: -restart 1 212ca4445c7SIlya Fursov args: -dtpost {{0 0.31}} 213*fe4ad979SIlya Fursov args: -ts_event_post_event_step {{-1 0.25}} 214*fe4ad979SIlya Fursov args: -ts_type rk 215ca4445c7SIlya Fursov args: -ts_adapt_type {{none basic}} 216*fe4ad979SIlya Fursov nsize: 2 217ca4445c7SIlya Fursov filter: sort 218ca4445c7SIlya Fursov filter_output: sort 219ca4445c7SIlya Fursov 220ca4445c7SIlya Fursov test: 221ca4445c7SIlya Fursov suffix: n 222*fe4ad979SIlya Fursov requires: !single 223ca4445c7SIlya Fursov output_file: output/ex1sin_n.out 224ca4445c7SIlya Fursov args: -dir -1 225ca4445c7SIlya Fursov args: -restart {{0 1}} 226ca4445c7SIlya Fursov args: -dtpost {{0 0.25}} 227*fe4ad979SIlya Fursov args: -ts_event_post_event_step 0.31 228*fe4ad979SIlya Fursov args: -ts_type {{beuler rk}} 229*fe4ad979SIlya Fursov args: -ts_adapt_type basic 230*fe4ad979SIlya Fursov nsize: 4 231*fe4ad979SIlya Fursov filter: sort 232*fe4ad979SIlya Fursov filter_output: sort 233*fe4ad979SIlya Fursov 234*fe4ad979SIlya Fursov test: 235*fe4ad979SIlya Fursov suffix: 0single 236*fe4ad979SIlya Fursov requires: single 237*fe4ad979SIlya Fursov output_file: output/ex1sin_0.out 238*fe4ad979SIlya Fursov args: -dir 0 -ts_event_dt_min 1e-6 -errtol 5e-5 239*fe4ad979SIlya Fursov args: -restart 1 240*fe4ad979SIlya Fursov args: -dtpost {{0 0.25}} 241*fe4ad979SIlya Fursov args: -ts_event_post_event_step {{-1 0.31}} 242*fe4ad979SIlya Fursov args: -ts_type beuler 243*fe4ad979SIlya Fursov args: -ts_adapt_type {{none basic}} 244*fe4ad979SIlya Fursov nsize: 4 245*fe4ad979SIlya Fursov filter: sort 246*fe4ad979SIlya Fursov filter_output: sort 247*fe4ad979SIlya Fursov 248*fe4ad979SIlya Fursov test: 249*fe4ad979SIlya Fursov suffix: psingle 250*fe4ad979SIlya Fursov requires: single 251*fe4ad979SIlya Fursov output_file: output/ex1sin_p.out 252*fe4ad979SIlya Fursov args: -dir 1 -ts_event_dt_min 1e-6 -errtol 5e-5 253*fe4ad979SIlya Fursov args: -restart 0 254*fe4ad979SIlya Fursov args: -dtpost {{0 0.31}} 255*fe4ad979SIlya Fursov args: -ts_event_post_event_step 0.25 256ca4445c7SIlya Fursov args: -ts_type {{beuler rk}} 257ca4445c7SIlya Fursov args: -ts_adapt_type {{none basic}} 258*fe4ad979SIlya Fursov nsize: 1 259*fe4ad979SIlya Fursov 260*fe4ad979SIlya Fursov test: 261*fe4ad979SIlya Fursov suffix: nsingle 262*fe4ad979SIlya Fursov requires: single 263*fe4ad979SIlya Fursov output_file: output/ex1sin_n.out 264*fe4ad979SIlya Fursov args: -dir -1 -ts_event_dt_min 1e-6 -errtol 5e-5 265*fe4ad979SIlya Fursov args: -restart 1 266*fe4ad979SIlya Fursov args: -dtpost {{0 0.25}} 267*fe4ad979SIlya Fursov args: -ts_event_post_event_step -1 268*fe4ad979SIlya Fursov args: -ts_type {{beuler rk}} 269*fe4ad979SIlya Fursov args: -ts_adapt_type {{none basic}} 270*fe4ad979SIlya Fursov nsize: 2 271ca4445c7SIlya Fursov filter: sort 272ca4445c7SIlya Fursov filter_output: sort 273ca4445c7SIlya Fursov TEST*/ 274