1 static char help[] = "Performs adjoint sensitivity analysis for a van der Pol like \n\ 2 equation with time dependent parameters using two approaches : \n\ 3 track : track only local sensitivities at each adjoint step \n\ 4 and accumulate them in a global array \n\ 5 global : track parameters at all timesteps together \n\ 6 Choose one of the two at runtime by -sa_method {track,global}. \n"; 7 8 /* 9 Concepts: TS^adjoint for time dependent parameters 10 Concepts: TS^Customized adjoint monitor based sensitivity tracking 11 Concepts: TS^All at once approach to sensitivity tracking 12 Processors: 1 13 */ 14 15 /* 16 Simple example to demonstrate TSAdjoint capabilities for time dependent params 17 without integral cost terms using either a tracking or global method. 18 19 Modify the Van Der Pol Eq to : 20 [u1'] = [mu1(t)*u1] 21 [u2'] = [mu2(t)*((1-u1^2)*u2-u1)] 22 (with initial conditions & params independent) 23 24 Define uref to be solution with initail conditions (2,-2/3), mu=(1,1e3) 25 - u_ref : (1.5967,-1.02969) 26 27 Define const function as cost = 2-norm(u - u_ref); 28 29 Initialization for the adjoint TS : 30 - dcost/dy|final_time = 2*(u-u_ref)|final_time 31 - dcost/dp|final_time = 0 32 33 The tracking method only tracks local sensitivity at each time step 34 and accumulates these sensitivities in a global array. Since the structure 35 of the equations being solved at each time step does not change, the jacobian 36 wrt parameters is defined analogous to constant RHSJacobian for a liner 37 TSSolve and the size of the jacP is independent of the number of time 38 steps. Enable this mode of adjoint analysis by -sa_method track. 39 40 The global method combines the parameters at all timesteps and tracks them 41 together. Thus, the columns of the jacP matrix are filled dependent upon the 42 time step. Also, the dimensions of the jacP matrix now depend upon the number 43 of time steps. Enable this mode of adjoint analysis by -sa_method global. 44 45 Since the equations here have parameters at predefined time steps, this 46 example should be run with non adaptive time stepping solvers only. This 47 can be ensured by -ts_adapt_type none (which is the default behavior only 48 for certain TS solvers like TSCN. If using an explicit TS like TSRK, 49 please be sure to add the aforementioned option to disable adaptive 50 timestepping.) 51 */ 52 53 /* 54 Include "petscts.h" so that we can use TS solvers. Note that this file 55 automatically includes: 56 petscsys.h - base PETSc routines petscvec.h - vectors 57 petscmat.h - matrices 58 petscis.h - index sets petscksp.h - Krylov subspace methods 59 petscviewer.h - viewers petscpc.h - preconditioners 60 petscksp.h - linear solvers petscsnes.h - nonlinear solvers 61 */ 62 #include <petscts.h> 63 64 extern PetscErrorCode RHSFunction(TS ,PetscReal ,Vec ,Vec ,void *); 65 extern PetscErrorCode RHSJacobian(TS ,PetscReal ,Vec ,Mat ,Mat ,void *); 66 extern PetscErrorCode RHSJacobianP_track(TS ,PetscReal ,Vec ,Mat ,void *); 67 extern PetscErrorCode RHSJacobianP_global(TS ,PetscReal ,Vec ,Mat ,void *); 68 extern PetscErrorCode Monitor(TS ,PetscInt ,PetscReal ,Vec ,void *); 69 extern PetscErrorCode AdjointMonitor(TS ,PetscInt ,PetscReal ,Vec ,PetscInt ,Vec *, Vec *,void *); 70 71 /* 72 User-defined application context - contains data needed by the 73 application-provided call-back routines. 74 */ 75 76 typedef struct { 77 /*------------- Forward solve data structures --------------*/ 78 PetscInt max_steps; /* number of steps to run ts for */ 79 PetscReal final_time; /* final time to integrate to*/ 80 PetscReal time_step; /* ts integration time step */ 81 Vec mu1; /* time dependent params */ 82 Vec mu2; /* time dependent params */ 83 Vec U; /* solution vector */ 84 Mat A; /* Jacobian matrix */ 85 86 /*------------- Adjoint solve data structures --------------*/ 87 Mat Jacp; /* JacobianP matrix */ 88 Vec lambda; /* adjoint variable */ 89 Vec mup; /* adjoint variable */ 90 91 /*------------- Global accumation vecs for monitor based tracking --------------*/ 92 Vec sens_mu1; /* global sensitivity accumulation */ 93 Vec sens_mu2; /* global sensitivity accumulation */ 94 PetscInt adj_idx; /* to keep track of adjoint solve index */ 95 } AppCtx; 96 97 typedef enum {SA_TRACK, SA_GLOBAL} SAMethod; 98 static const char *const SAMethods[] = {"TRACK","GLOBAL","SAMethod","SA_",0}; 99 100 /* ----------------------- Explicit form of the ODE -------------------- */ 101 102 PetscErrorCode RHSFunction(TS ts,PetscReal t,Vec U,Vec F,void *ctx) 103 { 104 AppCtx *user = (AppCtx*) ctx; 105 PetscScalar *f; 106 PetscInt curr_step; 107 const PetscScalar *u; 108 const PetscScalar *mu1; 109 const PetscScalar *mu2; 110 111 PetscFunctionBeginUser; 112 CHKERRQ(TSGetStepNumber(ts,&curr_step)); 113 CHKERRQ(VecGetArrayRead(U,&u)); 114 CHKERRQ(VecGetArrayRead(user->mu1,&mu1)); 115 CHKERRQ(VecGetArrayRead(user->mu2,&mu2)); 116 CHKERRQ(VecGetArray(F,&f)); 117 f[0] = mu1[curr_step]*u[1]; 118 f[1] = mu2[curr_step]*((1.-u[0]*u[0])*u[1]-u[0]); 119 CHKERRQ(VecRestoreArrayRead(U,&u)); 120 CHKERRQ(VecRestoreArrayRead(user->mu1,&mu1)); 121 CHKERRQ(VecRestoreArrayRead(user->mu2,&mu2)); 122 CHKERRQ(VecRestoreArray(F,&f)); 123 PetscFunctionReturn(0); 124 } 125 126 PetscErrorCode RHSJacobian(TS ts,PetscReal t,Vec U,Mat A,Mat B,void *ctx) 127 { 128 AppCtx *user = (AppCtx*) ctx; 129 PetscInt rowcol[] = {0,1}; 130 PetscScalar J[2][2]; 131 PetscInt curr_step; 132 const PetscScalar *u; 133 const PetscScalar *mu1; 134 const PetscScalar *mu2; 135 136 PetscFunctionBeginUser; 137 CHKERRQ(TSGetStepNumber(ts,&curr_step)); 138 CHKERRQ(VecGetArrayRead(user->mu1,&mu1)); 139 CHKERRQ(VecGetArrayRead(user->mu2,&mu2)); 140 CHKERRQ(VecGetArrayRead(U,&u)); 141 J[0][0] = 0; 142 J[1][0] = -mu2[curr_step]*(2.0*u[1]*u[0]+1.); 143 J[0][1] = mu1[curr_step]; 144 J[1][1] = mu2[curr_step]*(1.0-u[0]*u[0]); 145 CHKERRQ(MatSetValues(A,2,rowcol,2,rowcol,&J[0][0],INSERT_VALUES)); 146 CHKERRQ(MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY)); 147 CHKERRQ(MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY)); 148 CHKERRQ(VecRestoreArrayRead(U,&u)); 149 CHKERRQ(VecRestoreArrayRead(user->mu1,&mu1)); 150 CHKERRQ(VecRestoreArrayRead(user->mu2,&mu2)); 151 PetscFunctionReturn(0); 152 } 153 154 /* ------------------ Jacobian wrt parameters for tracking method ------------------ */ 155 156 PetscErrorCode RHSJacobianP_track(TS ts,PetscReal t,Vec U,Mat A,void *ctx) 157 { 158 PetscInt row[] = {0,1},col[] = {0,1}; 159 PetscScalar J[2][2]; 160 const PetscScalar *u; 161 162 PetscFunctionBeginUser; 163 CHKERRQ(VecGetArrayRead(U,&u)); 164 J[0][0] = u[1]; 165 J[1][0] = 0; 166 J[0][1] = 0; 167 J[1][1] = (1.-u[0]*u[0])*u[1]-u[0]; 168 CHKERRQ(MatSetValues(A,2,row,2,col,&J[0][0],INSERT_VALUES)); 169 CHKERRQ(MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY)); 170 CHKERRQ(MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY)); 171 CHKERRQ(VecRestoreArrayRead(U,&u)); 172 PetscFunctionReturn(0); 173 } 174 175 /* ------------------ Jacobian wrt parameters for global method ------------------ */ 176 177 PetscErrorCode RHSJacobianP_global(TS ts,PetscReal t,Vec U,Mat A,void *ctx) 178 { 179 PetscInt row[] = {0,1},col[] = {0,1}; 180 PetscScalar J[2][2]; 181 const PetscScalar *u; 182 PetscInt curr_step; 183 184 PetscFunctionBeginUser; 185 CHKERRQ(TSGetStepNumber(ts,&curr_step)); 186 CHKERRQ(VecGetArrayRead(U,&u)); 187 J[0][0] = u[1]; 188 J[1][0] = 0; 189 J[0][1] = 0; 190 J[1][1] = (1.-u[0]*u[0])*u[1]-u[0]; 191 col[0] = (curr_step)*2; 192 col[1] = (curr_step)*2+1; 193 CHKERRQ(MatSetValues(A,2,row,2,col,&J[0][0],INSERT_VALUES)); 194 CHKERRQ(MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY)); 195 CHKERRQ(MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY)); 196 CHKERRQ(VecRestoreArrayRead(U,&u)); 197 PetscFunctionReturn(0); 198 } 199 200 /* Dump solution to console if called */ 201 PetscErrorCode Monitor(TS ts,PetscInt step,PetscReal t,Vec U,void *ctx) 202 { 203 PetscFunctionBeginUser; 204 CHKERRQ(PetscPrintf(PETSC_COMM_WORLD,"\n Solution at time %e is \n", t)); 205 CHKERRQ(VecView(U,PETSC_VIEWER_STDOUT_WORLD)); 206 PetscFunctionReturn(0); 207 } 208 209 /* Customized adjoint monitor to keep track of local 210 sensitivities by storing them in a global sensitivity array. 211 Note : This routine is only used for the tracking method. */ 212 PetscErrorCode AdjointMonitor(TS ts,PetscInt steps,PetscReal time,Vec u,PetscInt numcost,Vec *lambda, Vec *mu,void *ctx) 213 { 214 AppCtx *user = (AppCtx*) ctx; 215 PetscInt curr_step; 216 PetscScalar *sensmu1_glob; 217 PetscScalar *sensmu2_glob; 218 const PetscScalar *sensmu_loc; 219 220 PetscFunctionBeginUser; 221 CHKERRQ(TSGetStepNumber(ts,&curr_step)); 222 /* Note that we skip the first call to the monitor in the adjoint 223 solve since the sensitivities are already set (during 224 initialization of adjoint vectors). 225 We also note that each indvidial TSAdjointSolve calls the monitor 226 twice, once at the step it is integrating from and once at the step 227 it integrates to. Only the second call is useful for transferring 228 local sensitivities to the global array. */ 229 if (curr_step == user->adj_idx) { 230 PetscFunctionReturn(0); 231 } else { 232 CHKERRQ(VecGetArrayRead(*mu,&sensmu_loc)); 233 CHKERRQ(VecGetArray(user->sens_mu1,&sensmu1_glob)); 234 CHKERRQ(VecGetArray(user->sens_mu2,&sensmu2_glob)); 235 sensmu1_glob[curr_step] = sensmu_loc[0]; 236 sensmu2_glob[curr_step] = sensmu_loc[1]; 237 CHKERRQ(VecRestoreArray(user->sens_mu1,&sensmu1_glob)); 238 CHKERRQ(VecRestoreArray(user->sens_mu2,&sensmu2_glob)); 239 CHKERRQ(VecRestoreArrayRead(*mu,&sensmu_loc)); 240 PetscFunctionReturn(0); 241 } 242 } 243 244 int main(int argc,char **argv) 245 { 246 TS ts; 247 AppCtx user; 248 PetscScalar *x_ptr,*y_ptr,*u_ptr; 249 PetscMPIInt size; 250 PetscBool monitor = PETSC_FALSE; 251 SAMethod sa = SA_GLOBAL; 252 PetscErrorCode ierr; 253 254 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 255 Initialize program 256 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 257 ierr = PetscInitialize(&argc,&argv,NULL,help);if (ierr) return ierr; 258 CHKERRMPI(MPI_Comm_size(PETSC_COMM_WORLD,&size)); 259 PetscCheck(size == 1,PETSC_COMM_WORLD,PETSC_ERR_WRONG_MPI_SIZE,"This is a uniprocessor example only!"); 260 261 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 262 Set runtime options 263 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 264 ierr = PetscOptionsBegin(PETSC_COMM_WORLD,NULL,"SA analysis options.","");CHKERRQ(ierr);{ 265 CHKERRQ(PetscOptionsGetBool(NULL,NULL,"-monitor",&monitor,NULL)); 266 CHKERRQ(PetscOptionsEnum("-sa_method","Sensitivity analysis method (track or global)","",SAMethods,(PetscEnum)sa,(PetscEnum*)&sa,NULL)); 267 } 268 ierr = PetscOptionsEnd();CHKERRQ(ierr); 269 270 user.final_time = 0.1; 271 user.max_steps = 5; 272 user.time_step = user.final_time/user.max_steps; 273 274 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 275 Create necessary matrix and vectors for forward solve. 276 Create Jacp matrix for adjoint solve. 277 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 278 CHKERRQ(VecCreateSeq(PETSC_COMM_WORLD,user.max_steps,&user.mu1)); 279 CHKERRQ(VecCreateSeq(PETSC_COMM_WORLD,user.max_steps,&user.mu2)); 280 CHKERRQ(VecSet(user.mu1,1.25)); 281 CHKERRQ(VecSet(user.mu2,1.0e2)); 282 283 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 284 For tracking method : create the global sensitivity array to 285 accumulate sensitivity with respect to parameters at each step. 286 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 287 if (sa == SA_TRACK) { 288 CHKERRQ(VecCreateSeq(PETSC_COMM_WORLD,user.max_steps,&user.sens_mu1)); 289 CHKERRQ(VecCreateSeq(PETSC_COMM_WORLD,user.max_steps,&user.sens_mu2)); 290 } 291 292 CHKERRQ(MatCreate(PETSC_COMM_WORLD,&user.A)); 293 CHKERRQ(MatSetSizes(user.A,PETSC_DECIDE,PETSC_DECIDE,2,2)); 294 CHKERRQ(MatSetFromOptions(user.A)); 295 CHKERRQ(MatSetUp(user.A)); 296 CHKERRQ(MatCreateVecs(user.A,&user.U,NULL)); 297 298 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 299 Note that the dimensions of the Jacp matrix depend upon the 300 sensitivity analysis method being used ! 301 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 302 CHKERRQ(MatCreate(PETSC_COMM_WORLD,&user.Jacp)); 303 if (sa == SA_TRACK) { 304 CHKERRQ(MatSetSizes(user.Jacp,PETSC_DECIDE,PETSC_DECIDE,2,2)); 305 } 306 if (sa == SA_GLOBAL) { 307 CHKERRQ(MatSetSizes(user.Jacp,PETSC_DECIDE,PETSC_DECIDE,2,user.max_steps*2)); 308 } 309 CHKERRQ(MatSetFromOptions(user.Jacp)); 310 CHKERRQ(MatSetUp(user.Jacp)); 311 312 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 313 Create timestepping solver context 314 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 315 CHKERRQ(TSCreate(PETSC_COMM_WORLD,&ts)); 316 CHKERRQ(TSSetEquationType(ts,TS_EQ_ODE_EXPLICIT)); 317 CHKERRQ(TSSetType(ts,TSCN)); 318 319 CHKERRQ(TSSetRHSFunction(ts,NULL,RHSFunction,&user)); 320 CHKERRQ(TSSetRHSJacobian(ts,user.A,user.A,RHSJacobian,&user)); 321 if (sa == SA_TRACK) { 322 CHKERRQ(TSSetRHSJacobianP(ts,user.Jacp,RHSJacobianP_track,&user)); 323 } 324 if (sa == SA_GLOBAL) { 325 CHKERRQ(TSSetRHSJacobianP(ts,user.Jacp,RHSJacobianP_global,&user)); 326 } 327 328 CHKERRQ(TSSetExactFinalTime(ts,TS_EXACTFINALTIME_MATCHSTEP)); 329 CHKERRQ(TSSetMaxTime(ts,user.final_time)); 330 CHKERRQ(TSSetTimeStep(ts,user.final_time/user.max_steps)); 331 332 if (monitor) { 333 CHKERRQ(TSMonitorSet(ts,Monitor,&user,NULL)); 334 } 335 if (sa == SA_TRACK) { 336 CHKERRQ(TSAdjointMonitorSet(ts,AdjointMonitor,&user,NULL)); 337 } 338 339 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 340 Set initial conditions 341 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 342 CHKERRQ(VecGetArray(user.U,&x_ptr)); 343 x_ptr[0] = 2.0; 344 x_ptr[1] = -2.0/3.0; 345 CHKERRQ(VecRestoreArray(user.U,&x_ptr)); 346 347 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 348 Save trajectory of solution so that TSAdjointSolve() may be used 349 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 350 CHKERRQ(TSSetSaveTrajectory(ts)); 351 352 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 353 Set runtime options 354 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 355 CHKERRQ(TSSetFromOptions(ts)); 356 357 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 358 Execute forward model and print solution. 359 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 360 CHKERRQ(TSSolve(ts,user.U)); 361 CHKERRQ(PetscPrintf(PETSC_COMM_WORLD,"\n Solution of forward TS :\n")); 362 CHKERRQ(VecView(user.U,PETSC_VIEWER_STDOUT_WORLD)); 363 CHKERRQ(PetscPrintf(PETSC_COMM_WORLD,"\n Forward TS solve successfull! Adjoint run begins!\n")); 364 365 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 366 Adjoint model starts here! Create adjoint vectors. 367 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 368 CHKERRQ(MatCreateVecs(user.A,&user.lambda,NULL)); 369 CHKERRQ(MatCreateVecs(user.Jacp,&user.mup,NULL)); 370 371 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 372 Set initial conditions for the adjoint vector 373 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 374 CHKERRQ(VecGetArray(user.U,&u_ptr)); 375 CHKERRQ(VecGetArray(user.lambda,&y_ptr)); 376 y_ptr[0] = 2*(u_ptr[0] - 1.5967); 377 y_ptr[1] = 2*(u_ptr[1] - -(1.02969)); 378 CHKERRQ(VecRestoreArray(user.lambda,&y_ptr)); 379 CHKERRQ(VecRestoreArray(user.U,&y_ptr)); 380 CHKERRQ(VecSet(user.mup,0)); 381 382 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 383 Set number of cost functions. 384 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 385 CHKERRQ(TSSetCostGradients(ts,1,&user.lambda,&user.mup)); 386 387 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 388 The adjoint vector mup has to be reset for each adjoint step when 389 using the tracking method as we want to treat the parameters at each 390 time step one at a time and prevent accumulation of the sensitivities 391 from parameters at previous time steps. 392 This is not necessary for the global method as each time dependent 393 parameter is treated as an independent parameter. 394 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 395 if (sa == SA_TRACK) { 396 for (user.adj_idx=user.max_steps; user.adj_idx>0; user.adj_idx--) { 397 CHKERRQ(VecSet(user.mup,0)); 398 CHKERRQ(TSAdjointSetSteps(ts, 1)); 399 CHKERRQ(TSAdjointSolve(ts)); 400 } 401 } 402 if (sa == SA_GLOBAL) { 403 CHKERRQ(TSAdjointSolve(ts)); 404 } 405 406 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 407 Dispaly adjoint sensitivities wrt parameters and initial conditions 408 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 409 if (sa == SA_TRACK) { 410 CHKERRQ(PetscPrintf(PETSC_COMM_WORLD,"\n sensitivity wrt mu1: d[cost]/d[mu1]\n")); 411 CHKERRQ(VecView(user.sens_mu1,PETSC_VIEWER_STDOUT_WORLD)); 412 CHKERRQ(PetscPrintf(PETSC_COMM_WORLD,"\n sensitivity wrt mu2: d[cost]/d[mu2]\n")); 413 CHKERRQ(VecView(user.sens_mu2,PETSC_VIEWER_STDOUT_WORLD)); 414 } 415 416 if (sa == SA_GLOBAL) { 417 ierr = PetscPrintf(PETSC_COMM_WORLD,"\n sensitivity wrt params: d[cost]/d[p], where p refers to \n\ 418 the interlaced vector made by combining mu1,mu2\n");CHKERRQ(ierr); 419 CHKERRQ(VecView(user.mup,PETSC_VIEWER_STDOUT_WORLD)); 420 } 421 422 CHKERRQ(PetscPrintf(PETSC_COMM_WORLD,"\n sensitivity wrt initial conditions: d[cost]/d[u(t=0)]\n")); 423 CHKERRQ(VecView(user.lambda,PETSC_VIEWER_STDOUT_WORLD)); 424 425 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 426 Free work space! 427 All PETSc objects should be destroyed when they are no longer needed. 428 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 429 CHKERRQ(MatDestroy(&user.A)); 430 CHKERRQ(MatDestroy(&user.Jacp)); 431 CHKERRQ(VecDestroy(&user.U)); 432 CHKERRQ(VecDestroy(&user.lambda)); 433 CHKERRQ(VecDestroy(&user.mup)); 434 CHKERRQ(VecDestroy(&user.mu1)); 435 CHKERRQ(VecDestroy(&user.mu2)); 436 if (sa == SA_TRACK) { 437 CHKERRQ(VecDestroy(&user.sens_mu1)); 438 CHKERRQ(VecDestroy(&user.sens_mu2)); 439 } 440 CHKERRQ(TSDestroy(&ts)); 441 ierr = PetscFinalize(); 442 return(ierr); 443 } 444 445 /*TEST 446 447 test: 448 requires: !complex 449 suffix : track 450 args : -sa_method track 451 452 test: 453 requires: !complex 454 suffix : global 455 args : -sa_method global 456 457 TEST*/ 458