1 /* Program usage: mpiexec -n 1 eptorsion1 [-help] [all TAO options] */ 2 3 /* ---------------------------------------------------------------------- 4 5 Elastic-plastic torsion problem. 6 7 The elastic plastic torsion problem arises from the determination 8 of the stress field on an infinitely long cylindrical bar, which is 9 equivalent to the solution of the following problem: 10 11 min{ .5 * integral(||gradient(v(x))||^2 dx) - C * integral(v(x) dx)} 12 13 where C is the torsion angle per unit length. 14 15 The multiprocessor version of this code is eptorsion2.c. 16 17 ---------------------------------------------------------------------- */ 18 19 /* 20 Include "petsctao.h" so that we can use TAO solvers. Note that this 21 file automatically includes files for lower-level support, such as those 22 provided by the PETSc library: 23 petsc.h - base PETSc routines petscvec.h - vectors 24 petscsys.h - system routines petscmat.h - matrices 25 petscis.h - index sets petscksp.h - Krylov subspace methods 26 petscviewer.h - viewers petscpc.h - preconditioners 27 */ 28 29 #include <petsctao.h> 30 31 static char help[] = "Demonstrates use of the TAO package to solve \n\ 32 unconstrained minimization problems on a single processor. This example \n\ 33 is based on the Elastic-Plastic Torsion (dept) problem from the MINPACK-2 \n\ 34 test suite.\n\ 35 The command line options are:\n\ 36 -mx <xg>, where <xg> = number of grid points in the 1st coordinate direction\n\ 37 -my <yg>, where <yg> = number of grid points in the 2nd coordinate direction\n\ 38 -par <param>, where <param> = angle of twist per unit length\n\n"; 39 40 /* 41 User-defined application context - contains data needed by the 42 application-provided call-back routines, FormFunction(), 43 FormGradient(), and FormHessian(). 44 */ 45 46 typedef struct { 47 PetscReal param; /* nonlinearity parameter */ 48 PetscInt mx, my; /* discretization in x- and y-directions */ 49 PetscInt ndim; /* problem dimension */ 50 Vec s, y, xvec; /* work space for computing Hessian */ 51 PetscReal hx, hy; /* mesh spacing in x- and y-directions */ 52 } AppCtx; 53 54 /* -------- User-defined Routines --------- */ 55 56 PetscErrorCode FormInitialGuess(AppCtx *, Vec); 57 PetscErrorCode FormFunction(Tao, Vec, PetscReal *, void *); 58 PetscErrorCode FormGradient(Tao, Vec, Vec, void *); 59 PetscErrorCode FormHessian(Tao, Vec, Mat, Mat, void *); 60 PetscErrorCode HessianProductMat(Mat, Vec, Vec); 61 PetscErrorCode HessianProduct(void *, Vec, Vec); 62 PetscErrorCode MatrixFreeHessian(Tao, Vec, Mat, Mat, void *); 63 PetscErrorCode FormFunctionGradient(Tao, Vec, PetscReal *, Vec, void *); 64 65 int main(int argc, char **argv) 66 { 67 PetscInt mx = 10; /* discretization in x-direction */ 68 PetscInt my = 10; /* discretization in y-direction */ 69 Vec x; /* solution, gradient vectors */ 70 PetscBool flg; /* A return value when checking for use options */ 71 Tao tao; /* Tao solver context */ 72 Mat H; /* Hessian matrix */ 73 AppCtx user; /* application context */ 74 PetscMPIInt size; /* number of processes */ 75 PetscReal one = 1.0; 76 77 PetscBool test_lmvm = PETSC_FALSE; 78 KSP ksp; 79 PC pc; 80 Mat M; 81 Vec in, out, out2; 82 PetscReal mult_solve_dist; 83 Vec ub, lb; 84 PetscInt i = 3; 85 86 /* Initialize TAO,PETSc */ 87 PetscFunctionBeginUser; 88 PetscCall(PetscInitialize(&argc, &argv, (char *)0, help)); 89 PetscCallMPI(MPI_Comm_size(MPI_COMM_WORLD, &size)); 90 PetscCheck(size == 1, PETSC_COMM_WORLD, PETSC_ERR_WRONG_MPI_SIZE, "Incorrect number of processors"); 91 92 /* Specify default parameters for the problem, check for command-line overrides */ 93 user.param = 5.0; 94 PetscCall(PetscOptionsGetInt(NULL, NULL, "-my", &my, &flg)); 95 PetscCall(PetscOptionsGetInt(NULL, NULL, "-mx", &mx, &flg)); 96 PetscCall(PetscOptionsGetReal(NULL, NULL, "-par", &user.param, &flg)); 97 PetscCall(PetscOptionsGetBool(NULL, NULL, "-test_lmvm", &test_lmvm, &flg)); 98 99 PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n---- Elastic-Plastic Torsion Problem -----\n")); 100 PetscCall(PetscPrintf(PETSC_COMM_SELF, "mx: %" PetscInt_FMT " my: %" PetscInt_FMT " \n\n", mx, my)); 101 user.ndim = mx * my; 102 user.mx = mx; 103 user.my = my; 104 user.hx = one / (mx + 1); 105 user.hy = one / (my + 1); 106 107 /* Allocate vectors */ 108 PetscCall(VecCreateSeq(PETSC_COMM_SELF, user.ndim, &user.y)); 109 PetscCall(VecDuplicate(user.y, &user.s)); 110 PetscCall(VecDuplicate(user.y, &x)); 111 PetscCall(VecDuplicate(user.y, &lb)); 112 PetscCall(VecDuplicate(user.y, &ub)); 113 114 PetscCall(VecSet(ub, 0.1)); 115 PetscCall(VecSet(lb, -0.1)); 116 PetscCall(VecSetValue(ub, i, 0., INSERT_VALUES)); 117 PetscCall(VecSetValue(lb, i, 0., INSERT_VALUES)); 118 PetscCall(VecAssemblyBegin(ub)); 119 PetscCall(VecAssemblyBegin(lb)); 120 PetscCall(VecAssemblyEnd(ub)); 121 PetscCall(VecAssemblyEnd(lb)); 122 123 /* Create TAO solver and set desired solution method */ 124 PetscCall(TaoCreate(PETSC_COMM_SELF, &tao)); 125 PetscCall(TaoSetType(tao, TAOLMVM)); 126 127 /* Set solution vector with an initial guess */ 128 PetscCall(FormInitialGuess(&user, x)); 129 PetscCall(TaoSetSolution(tao, x)); 130 PetscCall(TaoSetVariableBounds(tao, lb, ub)); 131 132 /* Set routine for function and gradient evaluation */ 133 PetscCall(TaoSetObjectiveAndGradient(tao, NULL, FormFunctionGradient, (void *)&user)); 134 135 /* From command line options, determine if using matrix-free hessian */ 136 PetscCall(PetscOptionsHasName(NULL, NULL, "-my_tao_mf", &flg)); 137 if (flg) { 138 PetscCall(MatCreateShell(PETSC_COMM_SELF, user.ndim, user.ndim, user.ndim, user.ndim, (void *)&user, &H)); 139 PetscCall(MatShellSetOperation(H, MATOP_MULT, (void (*)(void))HessianProductMat)); 140 PetscCall(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE)); 141 142 PetscCall(TaoSetHessian(tao, H, H, MatrixFreeHessian, (void *)&user)); 143 } else { 144 PetscCall(MatCreateSeqAIJ(PETSC_COMM_SELF, user.ndim, user.ndim, 5, NULL, &H)); 145 PetscCall(MatSetOption(H, MAT_SYMMETRIC, PETSC_TRUE)); 146 PetscCall(TaoSetHessian(tao, H, H, FormHessian, (void *)&user)); 147 } 148 149 /* Test the LMVM matrix */ 150 if (test_lmvm) { 151 PetscCall(PetscOptionsSetValue(NULL, "-tao_type", "bntr")); 152 PetscCall(PetscOptionsSetValue(NULL, "-tao_bnk_pc_type", "lmvm")); 153 } 154 155 /* Check for any TAO command line options */ 156 PetscCall(TaoSetFromOptions(tao)); 157 158 /* SOLVE THE APPLICATION */ 159 PetscCall(TaoSolve(tao)); 160 161 /* Test the LMVM matrix */ 162 if (test_lmvm) { 163 PetscCall(TaoGetKSP(tao, &ksp)); 164 PetscCall(KSPGetPC(ksp, &pc)); 165 PetscCall(PCLMVMGetMatLMVM(pc, &M)); 166 PetscCall(VecDuplicate(x, &in)); 167 PetscCall(VecDuplicate(x, &out)); 168 PetscCall(VecDuplicate(x, &out2)); 169 PetscCall(VecSet(in, 5.0)); 170 PetscCall(MatMult(M, in, out)); 171 PetscCall(MatSolve(M, out, out2)); 172 PetscCall(VecAXPY(out2, -1.0, in)); 173 PetscCall(VecNorm(out2, NORM_2, &mult_solve_dist)); 174 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)tao), "error between MatMult and MatSolve: %e\n", (double)mult_solve_dist)); 175 PetscCall(VecDestroy(&in)); 176 PetscCall(VecDestroy(&out)); 177 PetscCall(VecDestroy(&out2)); 178 } 179 180 PetscCall(TaoDestroy(&tao)); 181 PetscCall(VecDestroy(&user.s)); 182 PetscCall(VecDestroy(&user.y)); 183 PetscCall(VecDestroy(&x)); 184 PetscCall(MatDestroy(&H)); 185 PetscCall(VecDestroy(&lb)); 186 PetscCall(VecDestroy(&ub)); 187 188 PetscCall(PetscFinalize()); 189 return 0; 190 } 191 192 /* ------------------------------------------------------------------- */ 193 /* 194 FormInitialGuess - Computes an initial approximation to the solution. 195 196 Input Parameters: 197 . user - user-defined application context 198 . X - vector 199 200 Output Parameters: 201 . X - vector 202 */ 203 PetscErrorCode FormInitialGuess(AppCtx *user, Vec X) 204 { 205 PetscReal hx = user->hx, hy = user->hy, temp; 206 PetscReal val; 207 PetscInt i, j, k, nx = user->mx, ny = user->my; 208 209 /* Compute initial guess */ 210 PetscFunctionBeginUser; 211 for (j = 0; j < ny; j++) { 212 temp = PetscMin(j + 1, ny - j) * hy; 213 for (i = 0; i < nx; i++) { 214 k = nx * j + i; 215 val = PetscMin((PetscMin(i + 1, nx - i)) * hx, temp); 216 PetscCall(VecSetValues(X, 1, &k, &val, ADD_VALUES)); 217 } 218 } 219 PetscCall(VecAssemblyBegin(X)); 220 PetscCall(VecAssemblyEnd(X)); 221 PetscFunctionReturn(PETSC_SUCCESS); 222 } 223 224 /* ------------------------------------------------------------------- */ 225 /* 226 FormFunctionGradient - Evaluates the function and corresponding gradient. 227 228 Input Parameters: 229 tao - the Tao context 230 X - the input vector 231 ptr - optional user-defined context, as set by TaoSetFunction() 232 233 Output Parameters: 234 f - the newly evaluated function 235 G - the newly evaluated gradient 236 */ 237 PetscErrorCode FormFunctionGradient(Tao tao, Vec X, PetscReal *f, Vec G, void *ptr) 238 { 239 PetscFunctionBeginUser; 240 PetscCall(FormFunction(tao, X, f, ptr)); 241 PetscCall(FormGradient(tao, X, G, ptr)); 242 PetscFunctionReturn(PETSC_SUCCESS); 243 } 244 245 /* ------------------------------------------------------------------- */ 246 /* 247 FormFunction - Evaluates the function, f(X). 248 249 Input Parameters: 250 . tao - the Tao context 251 . X - the input vector 252 . ptr - optional user-defined context, as set by TaoSetFunction() 253 254 Output Parameters: 255 . f - the newly evaluated function 256 */ 257 PetscErrorCode FormFunction(Tao tao, Vec X, PetscReal *f, void *ptr) 258 { 259 AppCtx *user = (AppCtx *)ptr; 260 PetscReal hx = user->hx, hy = user->hy, area, three = 3.0, p5 = 0.5; 261 PetscReal zero = 0.0, vb, vl, vr, vt, dvdx, dvdy, flin = 0.0, fquad = 0.0; 262 PetscReal v, cdiv3 = user->param / three; 263 const PetscScalar *x; 264 PetscInt nx = user->mx, ny = user->my, i, j, k; 265 266 PetscFunctionBeginUser; 267 /* Get pointer to vector data */ 268 PetscCall(VecGetArrayRead(X, &x)); 269 270 /* Compute function contributions over the lower triangular elements */ 271 for (j = -1; j < ny; j++) { 272 for (i = -1; i < nx; i++) { 273 k = nx * j + i; 274 v = zero; 275 vr = zero; 276 vt = zero; 277 if (i >= 0 && j >= 0) v = x[k]; 278 if (i < nx - 1 && j > -1) vr = x[k + 1]; 279 if (i > -1 && j < ny - 1) vt = x[k + nx]; 280 dvdx = (vr - v) / hx; 281 dvdy = (vt - v) / hy; 282 fquad += dvdx * dvdx + dvdy * dvdy; 283 flin -= cdiv3 * (v + vr + vt); 284 } 285 } 286 287 /* Compute function contributions over the upper triangular elements */ 288 for (j = 0; j <= ny; j++) { 289 for (i = 0; i <= nx; i++) { 290 k = nx * j + i; 291 vb = zero; 292 vl = zero; 293 v = zero; 294 if (i < nx && j > 0) vb = x[k - nx]; 295 if (i > 0 && j < ny) vl = x[k - 1]; 296 if (i < nx && j < ny) v = x[k]; 297 dvdx = (v - vl) / hx; 298 dvdy = (v - vb) / hy; 299 fquad = fquad + dvdx * dvdx + dvdy * dvdy; 300 flin = flin - cdiv3 * (vb + vl + v); 301 } 302 } 303 304 /* Restore vector */ 305 PetscCall(VecRestoreArrayRead(X, &x)); 306 307 /* Scale the function */ 308 area = p5 * hx * hy; 309 *f = area * (p5 * fquad + flin); 310 311 PetscCall(PetscLogFlops(24.0 * nx * ny)); 312 PetscFunctionReturn(PETSC_SUCCESS); 313 } 314 315 /* ------------------------------------------------------------------- */ 316 /* 317 FormGradient - Evaluates the gradient, G(X). 318 319 Input Parameters: 320 . tao - the Tao context 321 . X - input vector 322 . ptr - optional user-defined context 323 324 Output Parameters: 325 . G - vector containing the newly evaluated gradient 326 */ 327 PetscErrorCode FormGradient(Tao tao, Vec X, Vec G, void *ptr) 328 { 329 AppCtx *user = (AppCtx *)ptr; 330 PetscReal zero = 0.0, p5 = 0.5, three = 3.0, area, val; 331 PetscInt nx = user->mx, ny = user->my, ind, i, j, k; 332 PetscReal hx = user->hx, hy = user->hy; 333 PetscReal vb, vl, vr, vt, dvdx, dvdy; 334 PetscReal v, cdiv3 = user->param / three; 335 const PetscScalar *x; 336 337 PetscFunctionBeginUser; 338 /* Initialize gradient to zero */ 339 PetscCall(VecSet(G, zero)); 340 341 /* Get pointer to vector data */ 342 PetscCall(VecGetArrayRead(X, &x)); 343 344 /* Compute gradient contributions over the lower triangular elements */ 345 for (j = -1; j < ny; j++) { 346 for (i = -1; i < nx; i++) { 347 k = nx * j + i; 348 v = zero; 349 vr = zero; 350 vt = zero; 351 if (i >= 0 && j >= 0) v = x[k]; 352 if (i < nx - 1 && j > -1) vr = x[k + 1]; 353 if (i > -1 && j < ny - 1) vt = x[k + nx]; 354 dvdx = (vr - v) / hx; 355 dvdy = (vt - v) / hy; 356 if (i != -1 && j != -1) { 357 ind = k; 358 val = -dvdx / hx - dvdy / hy - cdiv3; 359 PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES)); 360 } 361 if (i != nx - 1 && j != -1) { 362 ind = k + 1; 363 val = dvdx / hx - cdiv3; 364 PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES)); 365 } 366 if (i != -1 && j != ny - 1) { 367 ind = k + nx; 368 val = dvdy / hy - cdiv3; 369 PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES)); 370 } 371 } 372 } 373 374 /* Compute gradient contributions over the upper triangular elements */ 375 for (j = 0; j <= ny; j++) { 376 for (i = 0; i <= nx; i++) { 377 k = nx * j + i; 378 vb = zero; 379 vl = zero; 380 v = zero; 381 if (i < nx && j > 0) vb = x[k - nx]; 382 if (i > 0 && j < ny) vl = x[k - 1]; 383 if (i < nx && j < ny) v = x[k]; 384 dvdx = (v - vl) / hx; 385 dvdy = (v - vb) / hy; 386 if (i != nx && j != 0) { 387 ind = k - nx; 388 val = -dvdy / hy - cdiv3; 389 PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES)); 390 } 391 if (i != 0 && j != ny) { 392 ind = k - 1; 393 val = -dvdx / hx - cdiv3; 394 PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES)); 395 } 396 if (i != nx && j != ny) { 397 ind = k; 398 val = dvdx / hx + dvdy / hy - cdiv3; 399 PetscCall(VecSetValues(G, 1, &ind, &val, ADD_VALUES)); 400 } 401 } 402 } 403 PetscCall(VecRestoreArrayRead(X, &x)); 404 405 /* Assemble gradient vector */ 406 PetscCall(VecAssemblyBegin(G)); 407 PetscCall(VecAssemblyEnd(G)); 408 409 /* Scale the gradient */ 410 area = p5 * hx * hy; 411 PetscCall(VecScale(G, area)); 412 PetscCall(PetscLogFlops(24.0 * nx * ny)); 413 PetscFunctionReturn(PETSC_SUCCESS); 414 } 415 416 /* ------------------------------------------------------------------- */ 417 /* 418 FormHessian - Forms the Hessian matrix. 419 420 Input Parameters: 421 . tao - the Tao context 422 . X - the input vector 423 . ptr - optional user-defined context, as set by TaoSetHessian() 424 425 Output Parameters: 426 . H - Hessian matrix 427 . PrecH - optionally different preconditioning Hessian 428 . flag - flag indicating matrix structure 429 430 Notes: 431 This routine is intended simply as an example of the interface 432 to a Hessian evaluation routine. Since this example compute the 433 Hessian a column at a time, it is not particularly efficient and 434 is not recommended. 435 */ 436 PetscErrorCode FormHessian(Tao tao, Vec X, Mat H, Mat Hpre, void *ptr) 437 { 438 AppCtx *user = (AppCtx *)ptr; 439 PetscInt i, j, ndim = user->ndim; 440 PetscReal *y, zero = 0.0, one = 1.0; 441 PetscBool assembled; 442 443 PetscFunctionBeginUser; 444 user->xvec = X; 445 446 /* Initialize Hessian entries and work vector to zero */ 447 PetscCall(MatAssembled(H, &assembled)); 448 if (assembled) PetscCall(MatZeroEntries(H)); 449 450 PetscCall(VecSet(user->s, zero)); 451 452 /* Loop over matrix columns to compute entries of the Hessian */ 453 for (j = 0; j < ndim; j++) { 454 PetscCall(VecSetValues(user->s, 1, &j, &one, INSERT_VALUES)); 455 PetscCall(VecAssemblyBegin(user->s)); 456 PetscCall(VecAssemblyEnd(user->s)); 457 458 PetscCall(HessianProduct(ptr, user->s, user->y)); 459 460 PetscCall(VecSetValues(user->s, 1, &j, &zero, INSERT_VALUES)); 461 PetscCall(VecAssemblyBegin(user->s)); 462 PetscCall(VecAssemblyEnd(user->s)); 463 464 PetscCall(VecGetArray(user->y, &y)); 465 for (i = 0; i < ndim; i++) { 466 if (y[i] != zero) PetscCall(MatSetValues(H, 1, &i, 1, &j, &y[i], ADD_VALUES)); 467 } 468 PetscCall(VecRestoreArray(user->y, &y)); 469 } 470 PetscCall(MatAssemblyBegin(H, MAT_FINAL_ASSEMBLY)); 471 PetscCall(MatAssemblyEnd(H, MAT_FINAL_ASSEMBLY)); 472 PetscFunctionReturn(PETSC_SUCCESS); 473 } 474 475 /* ------------------------------------------------------------------- */ 476 /* 477 MatrixFreeHessian - Sets a pointer for use in computing Hessian-vector 478 products. 479 480 Input Parameters: 481 . tao - the Tao context 482 . X - the input vector 483 . ptr - optional user-defined context, as set by TaoSetHessian() 484 485 Output Parameters: 486 . H - Hessian matrix 487 . PrecH - optionally different preconditioning Hessian 488 . flag - flag indicating matrix structure 489 */ 490 PetscErrorCode MatrixFreeHessian(Tao tao, Vec X, Mat H, Mat PrecH, void *ptr) 491 { 492 AppCtx *user = (AppCtx *)ptr; 493 494 /* Sets location of vector for use in computing matrix-vector products of the form H(X)*y */ 495 PetscFunctionBeginUser; 496 user->xvec = X; 497 PetscFunctionReturn(PETSC_SUCCESS); 498 } 499 500 /* ------------------------------------------------------------------- */ 501 /* 502 HessianProductMat - Computes the matrix-vector product 503 y = mat*svec. 504 505 Input Parameters: 506 . mat - input matrix 507 . svec - input vector 508 509 Output Parameters: 510 . y - solution vector 511 */ 512 PetscErrorCode HessianProductMat(Mat mat, Vec svec, Vec y) 513 { 514 void *ptr; 515 516 PetscFunctionBeginUser; 517 PetscCall(MatShellGetContext(mat, &ptr)); 518 PetscCall(HessianProduct(ptr, svec, y)); 519 PetscFunctionReturn(PETSC_SUCCESS); 520 } 521 522 /* ------------------------------------------------------------------- */ 523 /* 524 Hessian Product - Computes the matrix-vector product: 525 y = f''(x)*svec. 526 527 Input Parameters: 528 . ptr - pointer to the user-defined context 529 . svec - input vector 530 531 Output Parameters: 532 . y - product vector 533 */ 534 PetscErrorCode HessianProduct(void *ptr, Vec svec, Vec y) 535 { 536 AppCtx *user = (AppCtx *)ptr; 537 PetscReal p5 = 0.5, zero = 0.0, one = 1.0, hx, hy, val, area; 538 const PetscScalar *x, *s; 539 PetscReal v, vb, vl, vr, vt, hxhx, hyhy; 540 PetscInt nx, ny, i, j, k, ind; 541 542 PetscFunctionBeginUser; 543 nx = user->mx; 544 ny = user->my; 545 hx = user->hx; 546 hy = user->hy; 547 hxhx = one / (hx * hx); 548 hyhy = one / (hy * hy); 549 550 /* Get pointers to vector data */ 551 PetscCall(VecGetArrayRead(user->xvec, &x)); 552 PetscCall(VecGetArrayRead(svec, &s)); 553 554 /* Initialize product vector to zero */ 555 PetscCall(VecSet(y, zero)); 556 557 /* Compute f''(x)*s over the lower triangular elements */ 558 for (j = -1; j < ny; j++) { 559 for (i = -1; i < nx; i++) { 560 k = nx * j + i; 561 v = zero; 562 vr = zero; 563 vt = zero; 564 if (i != -1 && j != -1) v = s[k]; 565 if (i != nx - 1 && j != -1) { 566 vr = s[k + 1]; 567 ind = k + 1; 568 val = hxhx * (vr - v); 569 PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES)); 570 } 571 if (i != -1 && j != ny - 1) { 572 vt = s[k + nx]; 573 ind = k + nx; 574 val = hyhy * (vt - v); 575 PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES)); 576 } 577 if (i != -1 && j != -1) { 578 ind = k; 579 val = hxhx * (v - vr) + hyhy * (v - vt); 580 PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES)); 581 } 582 } 583 } 584 585 /* Compute f''(x)*s over the upper triangular elements */ 586 for (j = 0; j <= ny; j++) { 587 for (i = 0; i <= nx; i++) { 588 k = nx * j + i; 589 v = zero; 590 vl = zero; 591 vb = zero; 592 if (i != nx && j != ny) v = s[k]; 593 if (i != nx && j != 0) { 594 vb = s[k - nx]; 595 ind = k - nx; 596 val = hyhy * (vb - v); 597 PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES)); 598 } 599 if (i != 0 && j != ny) { 600 vl = s[k - 1]; 601 ind = k - 1; 602 val = hxhx * (vl - v); 603 PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES)); 604 } 605 if (i != nx && j != ny) { 606 ind = k; 607 val = hxhx * (v - vl) + hyhy * (v - vb); 608 PetscCall(VecSetValues(y, 1, &ind, &val, ADD_VALUES)); 609 } 610 } 611 } 612 /* Restore vector data */ 613 PetscCall(VecRestoreArrayRead(svec, &s)); 614 PetscCall(VecRestoreArrayRead(user->xvec, &x)); 615 616 /* Assemble vector */ 617 PetscCall(VecAssemblyBegin(y)); 618 PetscCall(VecAssemblyEnd(y)); 619 620 /* Scale resulting vector by area */ 621 area = p5 * hx * hy; 622 PetscCall(VecScale(y, area)); 623 PetscCall(PetscLogFlops(18.0 * nx * ny)); 624 PetscFunctionReturn(PETSC_SUCCESS); 625 } 626 627 /*TEST 628 629 build: 630 requires: !complex 631 632 test: 633 args: -tao_monitor -tao_type bntr -tao_view -tao_bnk_ksp_monitor_short 634 635 TEST*/ 636