1 /* 2 This provides a simple shell for Fortran (and C programmers) to 3 create a very simple matrix class for use with KSP without coding 4 much of anything. 5 */ 6 7 #include <../src/mat/impls/shell/shell.h> /*I "petscmat.h" I*/ 8 9 /* 10 Store and scale values on zeroed rows 11 xx = [x_1, 0], 0 on zeroed columns 12 */ 13 static PetscErrorCode MatShellPreZeroRight(Mat A, Vec x, Vec *xx) 14 { 15 Mat_Shell *shell = (Mat_Shell *)A->data; 16 17 PetscFunctionBegin; 18 *xx = x; 19 if (shell->zrows) { 20 PetscCall(VecSet(shell->zvals_w, 0.0)); 21 PetscCall(VecScatterBegin(shell->zvals_sct_c, x, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD)); 22 PetscCall(VecScatterEnd(shell->zvals_sct_c, x, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD)); 23 PetscCall(VecPointwiseMult(shell->zvals_w, shell->zvals_w, shell->zvals)); 24 } 25 if (shell->zcols) { 26 if (!shell->right_work) PetscCall(MatCreateVecs(A, &shell->right_work, NULL)); 27 PetscCall(VecCopy(x, shell->right_work)); 28 PetscCall(VecISSet(shell->right_work, shell->zcols, 0.0)); 29 *xx = shell->right_work; 30 } 31 PetscFunctionReturn(PETSC_SUCCESS); 32 } 33 34 /* Insert properly diagonally scaled values stored in MatShellPreZeroRight */ 35 static PetscErrorCode MatShellPostZeroLeft(Mat A, Vec x) 36 { 37 Mat_Shell *shell = (Mat_Shell *)A->data; 38 39 PetscFunctionBegin; 40 if (shell->zrows) { 41 PetscCall(VecScatterBegin(shell->zvals_sct_r, shell->zvals_w, x, INSERT_VALUES, SCATTER_REVERSE)); 42 PetscCall(VecScatterEnd(shell->zvals_sct_r, shell->zvals_w, x, INSERT_VALUES, SCATTER_REVERSE)); 43 } 44 PetscFunctionReturn(PETSC_SUCCESS); 45 } 46 47 /* 48 Store and scale values on zeroed rows 49 xx = [x_1, 0], 0 on zeroed rows 50 */ 51 static PetscErrorCode MatShellPreZeroLeft(Mat A, Vec x, Vec *xx) 52 { 53 Mat_Shell *shell = (Mat_Shell *)A->data; 54 55 PetscFunctionBegin; 56 *xx = NULL; 57 if (!shell->zrows) { 58 *xx = x; 59 } else { 60 if (!shell->left_work) PetscCall(MatCreateVecs(A, NULL, &shell->left_work)); 61 PetscCall(VecCopy(x, shell->left_work)); 62 PetscCall(VecSet(shell->zvals_w, 0.0)); 63 PetscCall(VecScatterBegin(shell->zvals_sct_r, shell->zvals_w, shell->left_work, INSERT_VALUES, SCATTER_REVERSE)); 64 PetscCall(VecScatterEnd(shell->zvals_sct_r, shell->zvals_w, shell->left_work, INSERT_VALUES, SCATTER_REVERSE)); 65 PetscCall(VecScatterBegin(shell->zvals_sct_r, x, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD)); 66 PetscCall(VecScatterEnd(shell->zvals_sct_r, x, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD)); 67 PetscCall(VecPointwiseMult(shell->zvals_w, shell->zvals_w, shell->zvals)); 68 *xx = shell->left_work; 69 } 70 PetscFunctionReturn(PETSC_SUCCESS); 71 } 72 73 /* Zero zero-columns contributions, sum contributions from properly scaled values stored in MatShellPreZeroLeft */ 74 static PetscErrorCode MatShellPostZeroRight(Mat A, Vec x) 75 { 76 Mat_Shell *shell = (Mat_Shell *)A->data; 77 78 PetscFunctionBegin; 79 if (shell->zcols) PetscCall(VecISSet(x, shell->zcols, 0.0)); 80 if (shell->zrows) { 81 PetscCall(VecScatterBegin(shell->zvals_sct_c, shell->zvals_w, x, ADD_VALUES, SCATTER_REVERSE)); 82 PetscCall(VecScatterEnd(shell->zvals_sct_c, shell->zvals_w, x, ADD_VALUES, SCATTER_REVERSE)); 83 } 84 PetscFunctionReturn(PETSC_SUCCESS); 85 } 86 87 /* 88 xx = diag(left)*x 89 */ 90 static PetscErrorCode MatShellPreScaleLeft(Mat A, Vec x, Vec *xx, PetscBool conjugate) 91 { 92 Mat_Shell *shell = (Mat_Shell *)A->data; 93 94 PetscFunctionBegin; 95 *xx = NULL; 96 if (!shell->left) { 97 *xx = x; 98 } else { 99 if (!shell->left_work) PetscCall(VecDuplicate(shell->left, &shell->left_work)); 100 if (conjugate) { /* get arrays because there is no VecPointwiseMultConj() */ 101 PetscInt i, m; 102 const PetscScalar *d, *xarray; 103 PetscScalar *w; 104 PetscCall(VecGetLocalSize(x, &m)); 105 PetscCall(VecGetArrayRead(shell->left, &d)); 106 PetscCall(VecGetArrayRead(x, &xarray)); 107 PetscCall(VecGetArrayWrite(shell->left_work, &w)); 108 for (i = 0; i < m; i++) w[i] = PetscConj(d[i]) * xarray[i]; 109 PetscCall(VecRestoreArrayRead(shell->dshift, &d)); 110 PetscCall(VecRestoreArrayRead(x, &xarray)); 111 PetscCall(VecRestoreArrayWrite(shell->left_work, &w)); 112 } else PetscCall(VecPointwiseMult(shell->left_work, x, shell->left)); 113 *xx = shell->left_work; 114 } 115 PetscFunctionReturn(PETSC_SUCCESS); 116 } 117 118 /* 119 xx = diag(right)*x 120 */ 121 static PetscErrorCode MatShellPreScaleRight(Mat A, Vec x, Vec *xx) 122 { 123 Mat_Shell *shell = (Mat_Shell *)A->data; 124 125 PetscFunctionBegin; 126 *xx = NULL; 127 if (!shell->right) { 128 *xx = x; 129 } else { 130 if (!shell->right_work) PetscCall(VecDuplicate(shell->right, &shell->right_work)); 131 PetscCall(VecPointwiseMult(shell->right_work, x, shell->right)); 132 *xx = shell->right_work; 133 } 134 PetscFunctionReturn(PETSC_SUCCESS); 135 } 136 137 /* 138 x = diag(left)*x 139 */ 140 static PetscErrorCode MatShellPostScaleLeft(Mat A, Vec x) 141 { 142 Mat_Shell *shell = (Mat_Shell *)A->data; 143 144 PetscFunctionBegin; 145 if (shell->left) PetscCall(VecPointwiseMult(x, x, shell->left)); 146 PetscFunctionReturn(PETSC_SUCCESS); 147 } 148 149 /* 150 x = diag(right)*x 151 */ 152 static PetscErrorCode MatShellPostScaleRight(Mat A, Vec x, PetscBool conjugate) 153 { 154 Mat_Shell *shell = (Mat_Shell *)A->data; 155 156 PetscFunctionBegin; 157 if (shell->right) { 158 if (conjugate) { /* get arrays because there is no VecPointwiseMultConj() */ 159 PetscInt i, m; 160 const PetscScalar *d; 161 PetscScalar *xarray; 162 PetscCall(VecGetLocalSize(x, &m)); 163 PetscCall(VecGetArrayRead(shell->right, &d)); 164 PetscCall(VecGetArray(x, &xarray)); 165 for (i = 0; i < m; i++) xarray[i] = PetscConj(d[i]) * xarray[i]; 166 PetscCall(VecRestoreArrayRead(shell->dshift, &d)); 167 PetscCall(VecRestoreArray(x, &xarray)); 168 } else PetscCall(VecPointwiseMult(x, x, shell->right)); 169 } 170 PetscFunctionReturn(PETSC_SUCCESS); 171 } 172 173 /* 174 Y = vscale*Y + diag(dshift)*X + vshift*X 175 176 On input Y already contains A*x 177 178 If conjugate=PETSC_TRUE then vscale, dshift, and vshift are conjugated 179 */ 180 static PetscErrorCode MatShellShiftAndScale(Mat A, Vec X, Vec Y, PetscBool conjugate) 181 { 182 Mat_Shell *shell = (Mat_Shell *)A->data; 183 PetscScalar vscale = conjugate ? PetscConj(shell->vscale) : shell->vscale; 184 PetscScalar vshift = conjugate ? PetscConj(shell->vshift) : shell->vshift; 185 186 PetscFunctionBegin; 187 if (shell->dshift) { /* get arrays because there is no VecPointwiseMultAdd() */ 188 PetscInt i, m; 189 const PetscScalar *x, *d; 190 PetscScalar *y; 191 PetscCall(VecGetLocalSize(X, &m)); 192 PetscCall(VecGetArrayRead(shell->dshift, &d)); 193 PetscCall(VecGetArrayRead(X, &x)); 194 PetscCall(VecGetArray(Y, &y)); 195 if (conjugate) 196 for (i = 0; i < m; i++) y[i] = vscale * y[i] + PetscConj(d[i]) * x[i]; 197 else 198 for (i = 0; i < m; i++) y[i] = vscale * y[i] + d[i] * x[i]; 199 PetscCall(VecRestoreArrayRead(shell->dshift, &d)); 200 PetscCall(VecRestoreArrayRead(X, &x)); 201 PetscCall(VecRestoreArray(Y, &y)); 202 } else { 203 PetscCall(VecScale(Y, vscale)); 204 } 205 if (vshift != 0.0) PetscCall(VecAXPY(Y, vshift, X)); /* if test is for non-square matrices */ 206 PetscFunctionReturn(PETSC_SUCCESS); 207 } 208 209 static PetscErrorCode MatShellGetContext_Shell(Mat mat, void *ctx) 210 { 211 Mat_Shell *shell = (Mat_Shell *)mat->data; 212 213 PetscFunctionBegin; 214 if (shell->ctxcontainer) PetscCall(PetscContainerGetPointer(shell->ctxcontainer, (void **)ctx)); 215 else *(void **)ctx = NULL; 216 PetscFunctionReturn(PETSC_SUCCESS); 217 } 218 219 /*@ 220 MatShellGetContext - Returns the user-provided context associated with a `MATSHELL` shell matrix. 221 222 Not Collective 223 224 Input Parameter: 225 . mat - the matrix, should have been created with `MatCreateShell()` 226 227 Output Parameter: 228 . ctx - the user provided context 229 230 Level: advanced 231 232 Fortran Notes: 233 You must write a Fortran interface definition for this 234 function that tells Fortran the Fortran derived data type that you are passing in as the `ctx` argument. 235 236 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellSetOperation()`, `MatShellSetContext()` 237 @*/ 238 PetscErrorCode MatShellGetContext(Mat mat, void *ctx) 239 { 240 PetscFunctionBegin; 241 PetscValidHeaderSpecific(mat, MAT_CLASSID, 1); 242 PetscAssertPointer(ctx, 2); 243 PetscUseMethod(mat, "MatShellGetContext_C", (Mat, void *), (mat, ctx)); 244 PetscFunctionReturn(PETSC_SUCCESS); 245 } 246 247 static PetscErrorCode MatZeroRowsColumns_Local_Shell(Mat mat, PetscInt nr, PetscInt rows[], PetscInt nc, PetscInt cols[], PetscScalar diag, PetscBool rc) 248 { 249 Mat_Shell *shell = (Mat_Shell *)mat->data; 250 Vec x = NULL, b = NULL; 251 IS is1, is2; 252 const PetscInt *ridxs; 253 PetscInt *idxs, *gidxs; 254 PetscInt cum, rst, cst, i; 255 256 PetscFunctionBegin; 257 if (!shell->zvals) PetscCall(MatCreateVecs(mat, NULL, &shell->zvals)); 258 if (!shell->zvals_w) PetscCall(VecDuplicate(shell->zvals, &shell->zvals_w)); 259 PetscCall(MatGetOwnershipRange(mat, &rst, NULL)); 260 PetscCall(MatGetOwnershipRangeColumn(mat, &cst, NULL)); 261 262 /* Expand/create index set of zeroed rows */ 263 PetscCall(PetscMalloc1(nr, &idxs)); 264 for (i = 0; i < nr; i++) idxs[i] = rows[i] + rst; 265 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)mat), nr, idxs, PETSC_OWN_POINTER, &is1)); 266 PetscCall(ISSort(is1)); 267 PetscCall(VecISSet(shell->zvals, is1, diag)); 268 if (shell->zrows) { 269 PetscCall(ISSum(shell->zrows, is1, &is2)); 270 PetscCall(ISDestroy(&shell->zrows)); 271 PetscCall(ISDestroy(&is1)); 272 shell->zrows = is2; 273 } else shell->zrows = is1; 274 275 /* Create scatters for diagonal values communications */ 276 PetscCall(VecScatterDestroy(&shell->zvals_sct_c)); 277 PetscCall(VecScatterDestroy(&shell->zvals_sct_r)); 278 279 /* row scatter: from/to left vector */ 280 PetscCall(MatCreateVecs(mat, &x, &b)); 281 PetscCall(VecScatterCreate(b, shell->zrows, shell->zvals_w, shell->zrows, &shell->zvals_sct_r)); 282 283 /* col scatter: from right vector to left vector */ 284 PetscCall(ISGetIndices(shell->zrows, &ridxs)); 285 PetscCall(ISGetLocalSize(shell->zrows, &nr)); 286 PetscCall(PetscMalloc1(nr, &gidxs)); 287 for (i = 0, cum = 0; i < nr; i++) { 288 if (ridxs[i] >= mat->cmap->N) continue; 289 gidxs[cum] = ridxs[i]; 290 cum++; 291 } 292 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)mat), cum, gidxs, PETSC_OWN_POINTER, &is1)); 293 PetscCall(VecScatterCreate(x, is1, shell->zvals_w, is1, &shell->zvals_sct_c)); 294 PetscCall(ISDestroy(&is1)); 295 PetscCall(VecDestroy(&x)); 296 PetscCall(VecDestroy(&b)); 297 298 /* Expand/create index set of zeroed columns */ 299 if (rc) { 300 PetscCall(PetscMalloc1(nc, &idxs)); 301 for (i = 0; i < nc; i++) idxs[i] = cols[i] + cst; 302 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)mat), nc, idxs, PETSC_OWN_POINTER, &is1)); 303 PetscCall(ISSort(is1)); 304 if (shell->zcols) { 305 PetscCall(ISSum(shell->zcols, is1, &is2)); 306 PetscCall(ISDestroy(&shell->zcols)); 307 PetscCall(ISDestroy(&is1)); 308 shell->zcols = is2; 309 } else shell->zcols = is1; 310 } 311 PetscFunctionReturn(PETSC_SUCCESS); 312 } 313 314 static PetscErrorCode MatZeroRows_Shell(Mat mat, PetscInt n, const PetscInt rows[], PetscScalar diag, Vec x, Vec b) 315 { 316 Mat_Shell *shell = (Mat_Shell *)mat->data; 317 PetscInt nr, *lrows; 318 319 PetscFunctionBegin; 320 if (x && b) { 321 Vec xt; 322 PetscScalar *vals; 323 PetscInt *gcols, i, st, nl, nc; 324 325 PetscCall(PetscMalloc1(n, &gcols)); 326 for (i = 0, nc = 0; i < n; i++) 327 if (rows[i] < mat->cmap->N) gcols[nc++] = rows[i]; 328 329 PetscCall(MatCreateVecs(mat, &xt, NULL)); 330 PetscCall(VecCopy(x, xt)); 331 PetscCall(PetscCalloc1(nc, &vals)); 332 PetscCall(VecSetValues(xt, nc, gcols, vals, INSERT_VALUES)); /* xt = [x1, 0] */ 333 PetscCall(PetscFree(vals)); 334 PetscCall(VecAssemblyBegin(xt)); 335 PetscCall(VecAssemblyEnd(xt)); 336 PetscCall(VecAYPX(xt, -1.0, x)); /* xt = [0, x2] */ 337 338 PetscCall(VecGetOwnershipRange(xt, &st, NULL)); 339 PetscCall(VecGetLocalSize(xt, &nl)); 340 PetscCall(VecGetArray(xt, &vals)); 341 for (i = 0; i < nl; i++) { 342 PetscInt g = i + st; 343 if (g > mat->rmap->N) continue; 344 if (PetscAbsScalar(vals[i]) == 0.0) continue; 345 PetscCall(VecSetValue(b, g, diag * vals[i], INSERT_VALUES)); 346 } 347 PetscCall(VecRestoreArray(xt, &vals)); 348 PetscCall(VecAssemblyBegin(b)); 349 PetscCall(VecAssemblyEnd(b)); /* b = [b1, x2 * diag] */ 350 PetscCall(VecDestroy(&xt)); 351 PetscCall(PetscFree(gcols)); 352 } 353 PetscCall(PetscLayoutMapLocal(mat->rmap, n, rows, &nr, &lrows, NULL)); 354 PetscCall(MatZeroRowsColumns_Local_Shell(mat, nr, lrows, 0, NULL, diag, PETSC_FALSE)); 355 if (shell->axpy) PetscCall(MatZeroRows(shell->axpy, n, rows, 0.0, NULL, NULL)); 356 PetscCall(PetscFree(lrows)); 357 PetscFunctionReturn(PETSC_SUCCESS); 358 } 359 360 static PetscErrorCode MatZeroRowsColumns_Shell(Mat mat, PetscInt n, const PetscInt rowscols[], PetscScalar diag, Vec x, Vec b) 361 { 362 Mat_Shell *shell = (Mat_Shell *)mat->data; 363 PetscInt *lrows, *lcols; 364 PetscInt nr, nc; 365 PetscBool congruent; 366 367 PetscFunctionBegin; 368 if (x && b) { 369 Vec xt, bt; 370 PetscScalar *vals; 371 PetscInt *grows, *gcols, i, st, nl; 372 373 PetscCall(PetscMalloc2(n, &grows, n, &gcols)); 374 for (i = 0, nr = 0; i < n; i++) 375 if (rowscols[i] < mat->rmap->N) grows[nr++] = rowscols[i]; 376 for (i = 0, nc = 0; i < n; i++) 377 if (rowscols[i] < mat->cmap->N) gcols[nc++] = rowscols[i]; 378 PetscCall(PetscCalloc1(n, &vals)); 379 380 PetscCall(MatCreateVecs(mat, &xt, &bt)); 381 PetscCall(VecCopy(x, xt)); 382 PetscCall(VecSetValues(xt, nc, gcols, vals, INSERT_VALUES)); /* xt = [x1, 0] */ 383 PetscCall(VecAssemblyBegin(xt)); 384 PetscCall(VecAssemblyEnd(xt)); 385 PetscCall(VecAXPY(xt, -1.0, x)); /* xt = [0, -x2] */ 386 PetscCall(MatMult(mat, xt, bt)); /* bt = [-A12*x2,-A22*x2] */ 387 PetscCall(VecSetValues(bt, nr, grows, vals, INSERT_VALUES)); /* bt = [-A12*x2,0] */ 388 PetscCall(VecAssemblyBegin(bt)); 389 PetscCall(VecAssemblyEnd(bt)); 390 PetscCall(VecAXPY(b, 1.0, bt)); /* b = [b1 - A12*x2, b2] */ 391 PetscCall(VecSetValues(bt, nr, grows, vals, INSERT_VALUES)); /* b = [b1 - A12*x2, 0] */ 392 PetscCall(VecAssemblyBegin(bt)); 393 PetscCall(VecAssemblyEnd(bt)); 394 PetscCall(PetscFree(vals)); 395 396 PetscCall(VecGetOwnershipRange(xt, &st, NULL)); 397 PetscCall(VecGetLocalSize(xt, &nl)); 398 PetscCall(VecGetArray(xt, &vals)); 399 for (i = 0; i < nl; i++) { 400 PetscInt g = i + st; 401 if (g > mat->rmap->N) continue; 402 if (PetscAbsScalar(vals[i]) == 0.0) continue; 403 PetscCall(VecSetValue(b, g, -diag * vals[i], INSERT_VALUES)); 404 } 405 PetscCall(VecRestoreArray(xt, &vals)); 406 PetscCall(VecAssemblyBegin(b)); 407 PetscCall(VecAssemblyEnd(b)); /* b = [b1 - A12*x2, x2 * diag] */ 408 PetscCall(VecDestroy(&xt)); 409 PetscCall(VecDestroy(&bt)); 410 PetscCall(PetscFree2(grows, gcols)); 411 } 412 PetscCall(PetscLayoutMapLocal(mat->rmap, n, rowscols, &nr, &lrows, NULL)); 413 PetscCall(MatHasCongruentLayouts(mat, &congruent)); 414 if (congruent) { 415 nc = nr; 416 lcols = lrows; 417 } else { /* MatZeroRowsColumns implicitly assumes the rowscols indices are for a square matrix, here we handle a more general case */ 418 PetscInt i, nt, *t; 419 420 PetscCall(PetscMalloc1(n, &t)); 421 for (i = 0, nt = 0; i < n; i++) 422 if (rowscols[i] < mat->cmap->N) t[nt++] = rowscols[i]; 423 PetscCall(PetscLayoutMapLocal(mat->cmap, nt, t, &nc, &lcols, NULL)); 424 PetscCall(PetscFree(t)); 425 } 426 PetscCall(MatZeroRowsColumns_Local_Shell(mat, nr, lrows, nc, lcols, diag, PETSC_TRUE)); 427 if (!congruent) PetscCall(PetscFree(lcols)); 428 PetscCall(PetscFree(lrows)); 429 if (shell->axpy) PetscCall(MatZeroRowsColumns(shell->axpy, n, rowscols, 0.0, NULL, NULL)); 430 PetscFunctionReturn(PETSC_SUCCESS); 431 } 432 433 static PetscErrorCode MatDestroy_Shell(Mat mat) 434 { 435 Mat_Shell *shell = (Mat_Shell *)mat->data; 436 MatShellMatFunctionList matmat; 437 438 PetscFunctionBegin; 439 if (shell->ops->destroy) PetscCall((*shell->ops->destroy)(mat)); 440 PetscCall(PetscMemzero(shell->ops, sizeof(struct _MatShellOps))); 441 PetscCall(VecDestroy(&shell->left)); 442 PetscCall(VecDestroy(&shell->right)); 443 PetscCall(VecDestroy(&shell->dshift)); 444 PetscCall(VecDestroy(&shell->left_work)); 445 PetscCall(VecDestroy(&shell->right_work)); 446 PetscCall(VecDestroy(&shell->left_add_work)); 447 PetscCall(VecDestroy(&shell->right_add_work)); 448 PetscCall(VecDestroy(&shell->axpy_left)); 449 PetscCall(VecDestroy(&shell->axpy_right)); 450 PetscCall(MatDestroy(&shell->axpy)); 451 PetscCall(VecDestroy(&shell->zvals_w)); 452 PetscCall(VecDestroy(&shell->zvals)); 453 PetscCall(VecScatterDestroy(&shell->zvals_sct_c)); 454 PetscCall(VecScatterDestroy(&shell->zvals_sct_r)); 455 PetscCall(ISDestroy(&shell->zrows)); 456 PetscCall(ISDestroy(&shell->zcols)); 457 458 matmat = shell->matmat; 459 while (matmat) { 460 MatShellMatFunctionList next = matmat->next; 461 462 PetscCall(PetscObjectComposeFunction((PetscObject)mat, matmat->composedname, NULL)); 463 PetscCall(PetscFree(matmat->composedname)); 464 PetscCall(PetscFree(matmat->resultname)); 465 PetscCall(PetscFree(matmat)); 466 matmat = next; 467 } 468 PetscCall(MatShellSetContext(mat, NULL)); 469 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellGetContext_C", NULL)); 470 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetContext_C", NULL)); 471 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetContextDestroy_C", NULL)); 472 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetVecType_C", NULL)); 473 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetManageScalingShifts_C", NULL)); 474 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellGetScalingShifts_C", NULL)); 475 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetOperation_C", NULL)); 476 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellGetOperation_C", NULL)); 477 PetscCall(PetscObjectComposeFunction((PetscObject)mat, "MatShellSetMatProductOperation_C", NULL)); 478 PetscCall(PetscFree(mat->data)); 479 PetscFunctionReturn(PETSC_SUCCESS); 480 } 481 482 typedef struct { 483 PetscErrorCode (*numeric)(Mat, Mat, Mat, void *); 484 PetscErrorCode (*destroy)(void *); 485 void *userdata; 486 Mat B; 487 Mat Bt; 488 Mat axpy; 489 } MatMatDataShell; 490 491 static PetscErrorCode DestroyMatMatDataShell(void *data) 492 { 493 MatMatDataShell *mmdata = (MatMatDataShell *)data; 494 495 PetscFunctionBegin; 496 if (mmdata->destroy) PetscCall((*mmdata->destroy)(mmdata->userdata)); 497 PetscCall(MatDestroy(&mmdata->B)); 498 PetscCall(MatDestroy(&mmdata->Bt)); 499 PetscCall(MatDestroy(&mmdata->axpy)); 500 PetscCall(PetscFree(mmdata)); 501 PetscFunctionReturn(PETSC_SUCCESS); 502 } 503 504 static PetscErrorCode MatProductNumeric_Shell_X(Mat D) 505 { 506 Mat_Product *product; 507 Mat A, B; 508 MatMatDataShell *mdata; 509 PetscScalar zero = 0.0; 510 511 PetscFunctionBegin; 512 MatCheckProduct(D, 1); 513 product = D->product; 514 PetscCheck(product->data, PetscObjectComm((PetscObject)D), PETSC_ERR_PLIB, "Product data empty"); 515 A = product->A; 516 B = product->B; 517 mdata = (MatMatDataShell *)product->data; 518 if (mdata->numeric) { 519 Mat_Shell *shell = (Mat_Shell *)A->data; 520 PetscErrorCode (*stashsym)(Mat) = D->ops->productsymbolic; 521 PetscErrorCode (*stashnum)(Mat) = D->ops->productnumeric; 522 PetscBool useBmdata = PETSC_FALSE, newB = PETSC_TRUE; 523 524 if (shell->managescalingshifts) { 525 PetscCheck(!shell->zcols && !shell->zrows, PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProduct not supported with zeroed rows/columns"); 526 if (shell->right || shell->left) { 527 useBmdata = PETSC_TRUE; 528 if (!mdata->B) { 529 PetscCall(MatDuplicate(B, MAT_SHARE_NONZERO_PATTERN, &mdata->B)); 530 } else { 531 newB = PETSC_FALSE; 532 } 533 PetscCall(MatCopy(B, mdata->B, SAME_NONZERO_PATTERN)); 534 } 535 switch (product->type) { 536 case MATPRODUCT_AB: /* s L A R B + v L R B + L D R B */ 537 if (shell->right) PetscCall(MatDiagonalScale(mdata->B, shell->right, NULL)); 538 break; 539 case MATPRODUCT_AtB: /* s R A^t L B + v R L B + R D L B */ 540 if (shell->left) PetscCall(MatDiagonalScale(mdata->B, shell->left, NULL)); 541 break; 542 case MATPRODUCT_ABt: /* s L A R B^t + v L R B^t + L D R B^t */ 543 if (shell->right) PetscCall(MatDiagonalScale(mdata->B, NULL, shell->right)); 544 break; 545 case MATPRODUCT_RARt: /* s B L A R B^t + v B L R B^t + B L D R B^t */ 546 if (shell->right && shell->left) { 547 PetscBool flg; 548 549 PetscCall(VecEqual(shell->right, shell->left, &flg)); 550 PetscCheck(flg, PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices because left scaling != from right scaling", MatProductTypes[product->type], ((PetscObject)A)->type_name, 551 ((PetscObject)B)->type_name); 552 } 553 if (shell->right) PetscCall(MatDiagonalScale(mdata->B, NULL, shell->right)); 554 break; 555 case MATPRODUCT_PtAP: /* s B^t L A R B + v B^t L R B + B^t L D R B */ 556 if (shell->right && shell->left) { 557 PetscBool flg; 558 559 PetscCall(VecEqual(shell->right, shell->left, &flg)); 560 PetscCheck(flg, PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices because left scaling != from right scaling", MatProductTypes[product->type], ((PetscObject)A)->type_name, 561 ((PetscObject)B)->type_name); 562 } 563 if (shell->right) PetscCall(MatDiagonalScale(mdata->B, shell->right, NULL)); 564 break; 565 default: 566 SETERRQ(PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices", MatProductTypes[product->type], ((PetscObject)A)->type_name, ((PetscObject)B)->type_name); 567 } 568 } 569 /* allow the user to call MatMat operations on D */ 570 D->product = NULL; 571 D->ops->productsymbolic = NULL; 572 D->ops->productnumeric = NULL; 573 574 PetscCall((*mdata->numeric)(A, useBmdata ? mdata->B : B, D, mdata->userdata)); 575 576 /* clear any leftover user data and restore D pointers */ 577 PetscCall(MatProductClear(D)); 578 D->ops->productsymbolic = stashsym; 579 D->ops->productnumeric = stashnum; 580 D->product = product; 581 582 if (shell->managescalingshifts) { 583 PetscCall(MatScale(D, shell->vscale)); 584 switch (product->type) { 585 case MATPRODUCT_AB: /* s L A R B + v L R B + L D R B */ 586 case MATPRODUCT_ABt: /* s L A R B^t + v L R B^t + L D R B^t */ 587 if (shell->left) { 588 PetscCall(MatDiagonalScale(D, shell->left, NULL)); 589 if (shell->dshift || shell->vshift != zero) { 590 if (!shell->left_work) PetscCall(MatCreateVecs(A, NULL, &shell->left_work)); 591 if (shell->dshift) { 592 PetscCall(VecCopy(shell->dshift, shell->left_work)); 593 PetscCall(VecShift(shell->left_work, shell->vshift)); 594 PetscCall(VecPointwiseMult(shell->left_work, shell->left_work, shell->left)); 595 } else { 596 PetscCall(VecSet(shell->left_work, shell->vshift)); 597 } 598 if (product->type == MATPRODUCT_ABt) { 599 MatReuse reuse = mdata->Bt ? MAT_REUSE_MATRIX : MAT_INITIAL_MATRIX; 600 MatStructure str = mdata->Bt ? SUBSET_NONZERO_PATTERN : DIFFERENT_NONZERO_PATTERN; 601 602 PetscCall(MatTranspose(mdata->B, reuse, &mdata->Bt)); 603 PetscCall(MatDiagonalScale(mdata->Bt, shell->left_work, NULL)); 604 PetscCall(MatAXPY(D, 1.0, mdata->Bt, str)); 605 } else { 606 MatStructure str = newB ? DIFFERENT_NONZERO_PATTERN : SUBSET_NONZERO_PATTERN; 607 608 PetscCall(MatDiagonalScale(mdata->B, shell->left_work, NULL)); 609 PetscCall(MatAXPY(D, 1.0, mdata->B, str)); 610 } 611 } 612 } 613 break; 614 case MATPRODUCT_AtB: /* s R A^t L B + v R L B + R D L B */ 615 if (shell->right) { 616 PetscCall(MatDiagonalScale(D, shell->right, NULL)); 617 if (shell->dshift || shell->vshift != zero) { 618 MatStructure str = newB ? DIFFERENT_NONZERO_PATTERN : SUBSET_NONZERO_PATTERN; 619 620 if (!shell->right_work) PetscCall(MatCreateVecs(A, &shell->right_work, NULL)); 621 if (shell->dshift) { 622 PetscCall(VecCopy(shell->dshift, shell->right_work)); 623 PetscCall(VecShift(shell->right_work, shell->vshift)); 624 PetscCall(VecPointwiseMult(shell->right_work, shell->right_work, shell->right)); 625 } else { 626 PetscCall(VecSet(shell->right_work, shell->vshift)); 627 } 628 PetscCall(MatDiagonalScale(mdata->B, shell->right_work, NULL)); 629 PetscCall(MatAXPY(D, 1.0, mdata->B, str)); 630 } 631 } 632 break; 633 case MATPRODUCT_PtAP: /* s B^t L A R B + v B^t L R B + B^t L D R B */ 634 case MATPRODUCT_RARt: /* s B L A R B^t + v B L R B^t + B L D R B^t */ 635 PetscCheck(!shell->dshift && shell->vshift == zero, PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices with diagonal shift", MatProductTypes[product->type], ((PetscObject)A)->type_name, 636 ((PetscObject)B)->type_name); 637 break; 638 default: 639 SETERRQ(PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices", MatProductTypes[product->type], ((PetscObject)A)->type_name, ((PetscObject)B)->type_name); 640 } 641 if (shell->axpy && shell->axpy_vscale != zero) { 642 Mat X; 643 PetscObjectState axpy_state; 644 MatStructure str = DIFFERENT_NONZERO_PATTERN; /* not sure it is safe to ever use SUBSET_NONZERO_PATTERN */ 645 646 PetscCall(MatShellGetContext(shell->axpy, &X)); 647 PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state)); 648 PetscCheck(shell->axpy_state == axpy_state, PetscObjectComm((PetscObject)A), PETSC_ERR_ORDER, "Invalid AXPY state: cannot modify the X matrix passed to MatAXPY(Y,a,X,...)"); 649 if (!mdata->axpy) { 650 str = DIFFERENT_NONZERO_PATTERN; 651 PetscCall(MatProductCreate(shell->axpy, B, NULL, &mdata->axpy)); 652 PetscCall(MatProductSetType(mdata->axpy, product->type)); 653 PetscCall(MatProductSetFromOptions(mdata->axpy)); 654 PetscCall(MatProductSymbolic(mdata->axpy)); 655 } else { /* May be that shell->axpy has changed */ 656 PetscBool flg; 657 658 PetscCall(MatProductReplaceMats(shell->axpy, B, NULL, mdata->axpy)); 659 PetscCall(MatHasOperation(mdata->axpy, MATOP_PRODUCTSYMBOLIC, &flg)); 660 if (!flg) { 661 str = DIFFERENT_NONZERO_PATTERN; 662 PetscCall(MatProductSetFromOptions(mdata->axpy)); 663 PetscCall(MatProductSymbolic(mdata->axpy)); 664 } 665 } 666 PetscCall(MatProductNumeric(mdata->axpy)); 667 PetscCall(MatAXPY(D, shell->axpy_vscale, mdata->axpy, str)); 668 } 669 } 670 } else SETERRQ(PetscObjectComm((PetscObject)D), PETSC_ERR_PLIB, "Missing numeric operation"); 671 PetscFunctionReturn(PETSC_SUCCESS); 672 } 673 674 static PetscErrorCode MatProductSymbolic_Shell_X(Mat D) 675 { 676 Mat_Product *product; 677 Mat A, B; 678 MatShellMatFunctionList matmat; 679 Mat_Shell *shell; 680 PetscBool flg = PETSC_FALSE; 681 char composedname[256]; 682 MatMatDataShell *mdata; 683 684 PetscFunctionBegin; 685 MatCheckProduct(D, 1); 686 product = D->product; 687 PetscCheck(!product->data, PetscObjectComm((PetscObject)D), PETSC_ERR_PLIB, "Product data not empty"); 688 A = product->A; 689 B = product->B; 690 shell = (Mat_Shell *)A->data; 691 matmat = shell->matmat; 692 PetscCall(PetscSNPrintf(composedname, sizeof(composedname), "MatProductSetFromOptions_%s_%s_C", ((PetscObject)A)->type_name, ((PetscObject)B)->type_name)); 693 while (matmat) { 694 PetscCall(PetscStrcmp(composedname, matmat->composedname, &flg)); 695 flg = (PetscBool)(flg && (matmat->ptype == product->type)); 696 if (flg) break; 697 matmat = matmat->next; 698 } 699 PetscCheck(flg, PetscObjectComm((PetscObject)A), PETSC_ERR_PLIB, "Composedname \"%s\" for product type %s not found", composedname, MatProductTypes[product->type]); 700 switch (product->type) { 701 case MATPRODUCT_AB: 702 PetscCall(MatSetSizes(D, A->rmap->n, B->cmap->n, A->rmap->N, B->cmap->N)); 703 break; 704 case MATPRODUCT_AtB: 705 PetscCall(MatSetSizes(D, A->cmap->n, B->cmap->n, A->cmap->N, B->cmap->N)); 706 break; 707 case MATPRODUCT_ABt: 708 PetscCall(MatSetSizes(D, A->rmap->n, B->rmap->n, A->rmap->N, B->rmap->N)); 709 break; 710 case MATPRODUCT_RARt: 711 PetscCall(MatSetSizes(D, B->rmap->n, B->rmap->n, B->rmap->N, B->rmap->N)); 712 break; 713 case MATPRODUCT_PtAP: 714 PetscCall(MatSetSizes(D, B->cmap->n, B->cmap->n, B->cmap->N, B->cmap->N)); 715 break; 716 default: 717 SETERRQ(PetscObjectComm((PetscObject)D), PETSC_ERR_SUP, "MatProductSymbolic type %s not supported for %s and %s matrices", MatProductTypes[product->type], ((PetscObject)A)->type_name, ((PetscObject)B)->type_name); 718 } 719 /* respect users who passed in a matrix for which resultname is the base type */ 720 if (matmat->resultname) { 721 PetscCall(PetscObjectBaseTypeCompare((PetscObject)D, matmat->resultname, &flg)); 722 if (!flg) PetscCall(MatSetType(D, matmat->resultname)); 723 } 724 /* If matrix type was not set or different, we need to reset this pointers */ 725 D->ops->productsymbolic = MatProductSymbolic_Shell_X; 726 D->ops->productnumeric = MatProductNumeric_Shell_X; 727 /* attach product data */ 728 PetscCall(PetscNew(&mdata)); 729 mdata->numeric = matmat->numeric; 730 mdata->destroy = matmat->destroy; 731 if (matmat->symbolic) { 732 PetscCall((*matmat->symbolic)(A, B, D, &mdata->userdata)); 733 } else { /* call general setup if symbolic operation not provided */ 734 PetscCall(MatSetUp(D)); 735 } 736 PetscCheck(D->product, PetscObjectComm((PetscObject)D), PETSC_ERR_COR, "Product disappeared after user symbolic phase"); 737 PetscCheck(!D->product->data, PetscObjectComm((PetscObject)D), PETSC_ERR_COR, "Product data not empty after user symbolic phase"); 738 D->product->data = mdata; 739 D->product->destroy = DestroyMatMatDataShell; 740 /* Be sure to reset these pointers if the user did something unexpected */ 741 D->ops->productsymbolic = MatProductSymbolic_Shell_X; 742 D->ops->productnumeric = MatProductNumeric_Shell_X; 743 PetscFunctionReturn(PETSC_SUCCESS); 744 } 745 746 static PetscErrorCode MatProductSetFromOptions_Shell_X(Mat D) 747 { 748 Mat_Product *product; 749 Mat A, B; 750 MatShellMatFunctionList matmat; 751 Mat_Shell *shell; 752 PetscBool flg; 753 char composedname[256]; 754 755 PetscFunctionBegin; 756 MatCheckProduct(D, 1); 757 product = D->product; 758 A = product->A; 759 B = product->B; 760 PetscCall(MatIsShell(A, &flg)); 761 if (!flg) PetscFunctionReturn(PETSC_SUCCESS); 762 shell = (Mat_Shell *)A->data; 763 matmat = shell->matmat; 764 PetscCall(PetscSNPrintf(composedname, sizeof(composedname), "MatProductSetFromOptions_%s_%s_C", ((PetscObject)A)->type_name, ((PetscObject)B)->type_name)); 765 while (matmat) { 766 PetscCall(PetscStrcmp(composedname, matmat->composedname, &flg)); 767 flg = (PetscBool)(flg && (matmat->ptype == product->type)); 768 if (flg) break; 769 matmat = matmat->next; 770 } 771 if (flg) { 772 D->ops->productsymbolic = MatProductSymbolic_Shell_X; 773 } else PetscCall(PetscInfo(D, " symbolic product %s not registered for product type %s\n", composedname, MatProductTypes[product->type])); 774 PetscFunctionReturn(PETSC_SUCCESS); 775 } 776 777 static PetscErrorCode MatShellSetMatProductOperation_Private(Mat A, MatProductType ptype, PetscErrorCode (*symbolic)(Mat, Mat, Mat, void **), PetscErrorCode (*numeric)(Mat, Mat, Mat, void *), PetscErrorCode (*destroy)(void *), char *composedname, const char *resultname) 778 { 779 PetscBool flg; 780 Mat_Shell *shell; 781 MatShellMatFunctionList matmat; 782 783 PetscFunctionBegin; 784 PetscCheck(numeric, PetscObjectComm((PetscObject)A), PETSC_ERR_PLIB, "Missing numeric routine"); 785 PetscCheck(composedname, PetscObjectComm((PetscObject)A), PETSC_ERR_PLIB, "Missing composed name"); 786 787 /* add product callback */ 788 shell = (Mat_Shell *)A->data; 789 matmat = shell->matmat; 790 if (!matmat) { 791 PetscCall(PetscNew(&shell->matmat)); 792 matmat = shell->matmat; 793 } else { 794 MatShellMatFunctionList entry = matmat; 795 while (entry) { 796 PetscCall(PetscStrcmp(composedname, entry->composedname, &flg)); 797 flg = (PetscBool)(flg && (entry->ptype == ptype)); 798 matmat = entry; 799 if (flg) goto set; 800 entry = entry->next; 801 } 802 PetscCall(PetscNew(&matmat->next)); 803 matmat = matmat->next; 804 } 805 806 set: 807 matmat->symbolic = symbolic; 808 matmat->numeric = numeric; 809 matmat->destroy = destroy; 810 matmat->ptype = ptype; 811 PetscCall(PetscFree(matmat->composedname)); 812 PetscCall(PetscFree(matmat->resultname)); 813 PetscCall(PetscStrallocpy(composedname, &matmat->composedname)); 814 PetscCall(PetscStrallocpy(resultname, &matmat->resultname)); 815 PetscCall(PetscInfo(A, "Composing %s for product type %s with result %s\n", matmat->composedname, MatProductTypes[matmat->ptype], matmat->resultname ? matmat->resultname : "not specified")); 816 PetscCall(PetscObjectComposeFunction((PetscObject)A, matmat->composedname, MatProductSetFromOptions_Shell_X)); 817 PetscFunctionReturn(PETSC_SUCCESS); 818 } 819 820 /*@C 821 MatShellSetMatProductOperation - Allows user to set a matrix matrix operation for a `MATSHELL` shell matrix. 822 823 Logically Collective; No Fortran Support 824 825 Input Parameters: 826 + A - the `MATSHELL` shell matrix 827 . ptype - the product type 828 . symbolic - the function for the symbolic phase (can be `NULL`) 829 . numeric - the function for the numerical phase 830 . destroy - the function for the destruction of the needed data generated during the symbolic phase (can be `NULL`) 831 . Btype - the matrix type for the matrix to be multiplied against 832 - Ctype - the matrix type for the result (can be `NULL`) 833 834 Level: advanced 835 836 Example Usage: 837 .vb 838 extern PetscErrorCode usersymbolic(Mat, Mat, Mat, void**); 839 extern PetscErrorCode usernumeric(Mat, Mat, Mat, void*); 840 extern PetscErrorCode ctxdestroy(void*); 841 842 MatCreateShell(comm, m, n, M, N, ctx, &A); 843 MatShellSetMatProductOperation( 844 A, MATPRODUCT_AB, usersymbolic, usernumeric, ctxdestroy,MATSEQAIJ, MATDENSE 845 ); 846 // create B of type SEQAIJ etc.. 847 MatProductCreate(A, B, PETSC_NULLPTR, &C); 848 MatProductSetType(C, MATPRODUCT_AB); 849 MatProductSetFromOptions(C); 850 MatProductSymbolic(C); // actually runs the user defined symbolic operation 851 MatProductNumeric(C); // actually runs the user defined numeric operation 852 // use C = A * B 853 .ve 854 855 Notes: 856 `MATPRODUCT_ABC` is not supported yet. 857 858 If the symbolic phase is not specified, `MatSetUp()` is called on the result matrix that must have its type set if Ctype is `NULL`. 859 860 Any additional data needed by the matrix product needs to be returned during the symbolic phase and destroyed with the destroy callback. 861 PETSc will take care of calling the user-defined callbacks. 862 It is allowed to specify the same callbacks for different Btype matrix types. 863 The couple (Btype,ptype) uniquely identifies the operation, the last specified callbacks takes precedence. 864 865 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellSetContext()`, `MatSetOperation()`, `MatProductType`, `MatType`, `MatSetUp()` 866 @*/ 867 PetscErrorCode MatShellSetMatProductOperation(Mat A, MatProductType ptype, PetscErrorCode (*symbolic)(Mat, Mat, Mat, void **), PetscErrorCode (*numeric)(Mat, Mat, Mat, void *), PetscErrorCode (*destroy)(void *), MatType Btype, MatType Ctype) 868 { 869 PetscFunctionBegin; 870 PetscValidHeaderSpecific(A, MAT_CLASSID, 1); 871 PetscValidLogicalCollectiveEnum(A, ptype, 2); 872 PetscCheck(ptype != MATPRODUCT_ABC, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Not for product type %s", MatProductTypes[ptype]); 873 PetscCheck(numeric, PetscObjectComm((PetscObject)A), PETSC_ERR_USER, "Missing numeric routine, argument 4"); 874 PetscAssertPointer(Btype, 6); 875 if (Ctype) PetscAssertPointer(Ctype, 7); 876 PetscTryMethod(A, "MatShellSetMatProductOperation_C", (Mat, MatProductType, PetscErrorCode (*)(Mat, Mat, Mat, void **), PetscErrorCode (*)(Mat, Mat, Mat, void *), PetscErrorCode (*)(void *), MatType, MatType), (A, ptype, symbolic, numeric, destroy, Btype, Ctype)); 877 PetscFunctionReturn(PETSC_SUCCESS); 878 } 879 880 static PetscErrorCode MatShellSetMatProductOperation_Shell(Mat A, MatProductType ptype, PetscErrorCode (*symbolic)(Mat, Mat, Mat, void **), PetscErrorCode (*numeric)(Mat, Mat, Mat, void *), PetscErrorCode (*destroy)(void *), MatType Btype, MatType Ctype) 881 { 882 PetscBool flg; 883 char composedname[256]; 884 MatRootName Bnames = MatRootNameList, Cnames = MatRootNameList; 885 PetscMPIInt size; 886 887 PetscFunctionBegin; 888 PetscValidType(A, 1); 889 while (Bnames) { /* user passed in the root name */ 890 PetscCall(PetscStrcmp(Btype, Bnames->rname, &flg)); 891 if (flg) break; 892 Bnames = Bnames->next; 893 } 894 while (Cnames) { /* user passed in the root name */ 895 PetscCall(PetscStrcmp(Ctype, Cnames->rname, &flg)); 896 if (flg) break; 897 Cnames = Cnames->next; 898 } 899 PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)A), &size)); 900 Btype = Bnames ? (size > 1 ? Bnames->mname : Bnames->sname) : Btype; 901 Ctype = Cnames ? (size > 1 ? Cnames->mname : Cnames->sname) : Ctype; 902 PetscCall(PetscSNPrintf(composedname, sizeof(composedname), "MatProductSetFromOptions_%s_%s_C", ((PetscObject)A)->type_name, Btype)); 903 PetscCall(MatShellSetMatProductOperation_Private(A, ptype, symbolic, numeric, destroy, composedname, Ctype)); 904 PetscFunctionReturn(PETSC_SUCCESS); 905 } 906 907 static PetscErrorCode MatCopy_Shell(Mat A, Mat B, MatStructure str) 908 { 909 Mat_Shell *shellA = (Mat_Shell *)A->data, *shellB = (Mat_Shell *)B->data; 910 PetscBool matflg; 911 MatShellMatFunctionList matmatA; 912 913 PetscFunctionBegin; 914 PetscCall(MatIsShell(B, &matflg)); 915 PetscCheck(matflg, PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONG, "Matrix %s not derived from MATSHELL", ((PetscObject)B)->type_name); 916 917 B->ops[0] = A->ops[0]; 918 shellB->ops[0] = shellA->ops[0]; 919 920 if (shellA->ops->copy) PetscCall((*shellA->ops->copy)(A, B, str)); 921 shellB->vscale = shellA->vscale; 922 shellB->vshift = shellA->vshift; 923 if (shellA->dshift) { 924 if (!shellB->dshift) PetscCall(VecDuplicate(shellA->dshift, &shellB->dshift)); 925 PetscCall(VecCopy(shellA->dshift, shellB->dshift)); 926 } else { 927 PetscCall(VecDestroy(&shellB->dshift)); 928 } 929 if (shellA->left) { 930 if (!shellB->left) PetscCall(VecDuplicate(shellA->left, &shellB->left)); 931 PetscCall(VecCopy(shellA->left, shellB->left)); 932 } else { 933 PetscCall(VecDestroy(&shellB->left)); 934 } 935 if (shellA->right) { 936 if (!shellB->right) PetscCall(VecDuplicate(shellA->right, &shellB->right)); 937 PetscCall(VecCopy(shellA->right, shellB->right)); 938 } else { 939 PetscCall(VecDestroy(&shellB->right)); 940 } 941 PetscCall(MatDestroy(&shellB->axpy)); 942 shellB->axpy_vscale = 0.0; 943 shellB->axpy_state = 0; 944 if (shellA->axpy) { 945 PetscCall(PetscObjectReference((PetscObject)shellA->axpy)); 946 shellB->axpy = shellA->axpy; 947 shellB->axpy_vscale = shellA->axpy_vscale; 948 shellB->axpy_state = shellA->axpy_state; 949 } 950 if (shellA->zrows) { 951 PetscCall(ISDuplicate(shellA->zrows, &shellB->zrows)); 952 if (shellA->zcols) PetscCall(ISDuplicate(shellA->zcols, &shellB->zcols)); 953 PetscCall(VecDuplicate(shellA->zvals, &shellB->zvals)); 954 PetscCall(VecCopy(shellA->zvals, shellB->zvals)); 955 PetscCall(VecDuplicate(shellA->zvals_w, &shellB->zvals_w)); 956 PetscCall(PetscObjectReference((PetscObject)shellA->zvals_sct_r)); 957 PetscCall(PetscObjectReference((PetscObject)shellA->zvals_sct_c)); 958 shellB->zvals_sct_r = shellA->zvals_sct_r; 959 shellB->zvals_sct_c = shellA->zvals_sct_c; 960 } 961 962 matmatA = shellA->matmat; 963 if (matmatA) { 964 while (matmatA->next) { 965 PetscCall(MatShellSetMatProductOperation_Private(B, matmatA->ptype, matmatA->symbolic, matmatA->numeric, matmatA->destroy, matmatA->composedname, matmatA->resultname)); 966 matmatA = matmatA->next; 967 } 968 } 969 PetscFunctionReturn(PETSC_SUCCESS); 970 } 971 972 static PetscErrorCode MatDuplicate_Shell(Mat mat, MatDuplicateOption op, Mat *M) 973 { 974 PetscFunctionBegin; 975 PetscCall(MatCreateShell(PetscObjectComm((PetscObject)mat), mat->rmap->n, mat->cmap->n, mat->rmap->N, mat->cmap->N, NULL, M)); 976 ((Mat_Shell *)(*M)->data)->ctxcontainer = ((Mat_Shell *)mat->data)->ctxcontainer; 977 PetscCall(PetscObjectCompose((PetscObject)*M, "MatShell ctx", (PetscObject)((Mat_Shell *)(*M)->data)->ctxcontainer)); 978 PetscCall(PetscObjectChangeTypeName((PetscObject)*M, ((PetscObject)mat)->type_name)); 979 if (op == MAT_COPY_VALUES) PetscCall(MatCopy(mat, *M, SAME_NONZERO_PATTERN)); 980 PetscCall(PetscObjectCopyFortranFunctionPointers((PetscObject)mat, (PetscObject)*M)); 981 PetscFunctionReturn(PETSC_SUCCESS); 982 } 983 984 static PetscErrorCode MatMult_Shell(Mat A, Vec x, Vec y) 985 { 986 Mat_Shell *shell = (Mat_Shell *)A->data; 987 Vec xx; 988 PetscObjectState instate, outstate; 989 990 PetscFunctionBegin; 991 PetscCall(MatShellPreZeroRight(A, x, &xx)); 992 PetscCall(MatShellPreScaleRight(A, xx, &xx)); 993 PetscCall(PetscObjectStateGet((PetscObject)y, &instate)); 994 PetscCall((*shell->ops->mult)(A, xx, y)); 995 PetscCall(PetscObjectStateGet((PetscObject)y, &outstate)); 996 if (instate == outstate) { 997 /* increase the state of the output vector since the user did not update its state themself as should have been done */ 998 PetscCall(PetscObjectStateIncrease((PetscObject)y)); 999 } 1000 PetscCall(MatShellShiftAndScale(A, xx, y, PETSC_FALSE)); 1001 PetscCall(MatShellPostScaleLeft(A, y)); 1002 PetscCall(MatShellPostZeroLeft(A, y)); 1003 1004 if (shell->axpy) { 1005 Mat X; 1006 PetscObjectState axpy_state; 1007 1008 PetscCall(MatShellGetContext(shell->axpy, &X)); 1009 PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state)); 1010 PetscCheck(shell->axpy_state == axpy_state, PetscObjectComm((PetscObject)A), PETSC_ERR_ORDER, "Invalid AXPY state: cannot modify the X matrix passed to MatAXPY(Y,a,X,...)"); 1011 1012 PetscCall(MatCreateVecs(shell->axpy, shell->axpy_right ? NULL : &shell->axpy_right, shell->axpy_left ? NULL : &shell->axpy_left)); 1013 PetscCall(VecCopy(x, shell->axpy_right)); 1014 PetscCall(MatMult(shell->axpy, shell->axpy_right, shell->axpy_left)); 1015 PetscCall(VecAXPY(y, shell->axpy_vscale, shell->axpy_left)); 1016 } 1017 PetscFunctionReturn(PETSC_SUCCESS); 1018 } 1019 1020 static PetscErrorCode MatMultAdd_Shell(Mat A, Vec x, Vec y, Vec z) 1021 { 1022 Mat_Shell *shell = (Mat_Shell *)A->data; 1023 1024 PetscFunctionBegin; 1025 if (y == z) { 1026 if (!shell->right_add_work) PetscCall(VecDuplicate(z, &shell->right_add_work)); 1027 PetscCall(MatMult(A, x, shell->right_add_work)); 1028 PetscCall(VecAXPY(z, 1.0, shell->right_add_work)); 1029 } else { 1030 PetscCall(MatMult(A, x, z)); 1031 PetscCall(VecAXPY(z, 1.0, y)); 1032 } 1033 PetscFunctionReturn(PETSC_SUCCESS); 1034 } 1035 1036 static PetscErrorCode MatMultTranspose_Shell(Mat A, Vec x, Vec y) 1037 { 1038 Mat_Shell *shell = (Mat_Shell *)A->data; 1039 Vec xx; 1040 PetscObjectState instate, outstate; 1041 1042 PetscFunctionBegin; 1043 PetscCall(MatShellPreZeroLeft(A, x, &xx)); 1044 PetscCall(MatShellPreScaleLeft(A, xx, &xx, PETSC_FALSE)); 1045 PetscCall(PetscObjectStateGet((PetscObject)y, &instate)); 1046 PetscCall((*shell->ops->multtranspose)(A, xx, y)); 1047 PetscCall(PetscObjectStateGet((PetscObject)y, &outstate)); 1048 if (instate == outstate) { 1049 /* increase the state of the output vector since the user did not update its state themself as should have been done */ 1050 PetscCall(PetscObjectStateIncrease((PetscObject)y)); 1051 } 1052 PetscCall(MatShellShiftAndScale(A, xx, y, PETSC_FALSE)); 1053 PetscCall(MatShellPostScaleRight(A, y, PETSC_FALSE)); 1054 PetscCall(MatShellPostZeroRight(A, y)); 1055 1056 if (shell->axpy) { 1057 Mat X; 1058 PetscObjectState axpy_state; 1059 1060 PetscCall(MatShellGetContext(shell->axpy, &X)); 1061 PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state)); 1062 PetscCheck(shell->axpy_state == axpy_state, PetscObjectComm((PetscObject)A), PETSC_ERR_ORDER, "Invalid AXPY state: cannot modify the X matrix passed to MatAXPY(Y,a,X,...)"); 1063 PetscCall(MatCreateVecs(shell->axpy, shell->axpy_right ? NULL : &shell->axpy_right, shell->axpy_left ? NULL : &shell->axpy_left)); 1064 PetscCall(VecCopy(x, shell->axpy_left)); 1065 PetscCall(MatMultTranspose(shell->axpy, shell->axpy_left, shell->axpy_right)); 1066 PetscCall(VecAXPY(y, shell->axpy_vscale, shell->axpy_right)); 1067 } 1068 PetscFunctionReturn(PETSC_SUCCESS); 1069 } 1070 1071 static PetscErrorCode MatMultHermitianTranspose_Shell(Mat A, Vec x, Vec y) 1072 { 1073 Mat_Shell *shell = (Mat_Shell *)A->data; 1074 Vec xx; 1075 PetscObjectState instate, outstate; 1076 1077 PetscFunctionBegin; 1078 PetscCall(MatShellPreZeroLeft(A, x, &xx)); 1079 PetscCall(MatShellPreScaleLeft(A, xx, &xx, PETSC_TRUE)); 1080 PetscCall(PetscObjectStateGet((PetscObject)y, &instate)); 1081 PetscCall((*shell->ops->multhermitiantranspose)(A, xx, y)); 1082 PetscCall(PetscObjectStateGet((PetscObject)y, &outstate)); 1083 if (instate == outstate) { 1084 /* increase the state of the output vector since the user did not update its state themself as should have been done */ 1085 PetscCall(PetscObjectStateIncrease((PetscObject)y)); 1086 } 1087 PetscCall(MatShellShiftAndScale(A, xx, y, PETSC_TRUE)); 1088 PetscCall(MatShellPostScaleRight(A, y, PETSC_TRUE)); 1089 PetscCall(MatShellPostZeroRight(A, y)); 1090 1091 if (shell->axpy) { 1092 Mat X; 1093 PetscObjectState axpy_state; 1094 1095 PetscCall(MatShellGetContext(shell->axpy, &X)); 1096 PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state)); 1097 PetscCheck(shell->axpy_state == axpy_state, PetscObjectComm((PetscObject)A), PETSC_ERR_ORDER, "Invalid AXPY state: cannot modify the X matrix passed to MatAXPY(Y,a,X,...)"); 1098 PetscCall(MatCreateVecs(shell->axpy, shell->axpy_right ? NULL : &shell->axpy_right, shell->axpy_left ? NULL : &shell->axpy_left)); 1099 PetscCall(VecCopy(x, shell->axpy_left)); 1100 PetscCall(MatMultHermitianTranspose(shell->axpy, shell->axpy_left, shell->axpy_right)); 1101 PetscCall(VecAXPY(y, PetscConj(shell->axpy_vscale), shell->axpy_right)); 1102 } 1103 PetscFunctionReturn(PETSC_SUCCESS); 1104 } 1105 1106 static PetscErrorCode MatMultTransposeAdd_Shell(Mat A, Vec x, Vec y, Vec z) 1107 { 1108 Mat_Shell *shell = (Mat_Shell *)A->data; 1109 1110 PetscFunctionBegin; 1111 if (y == z) { 1112 if (!shell->left_add_work) PetscCall(VecDuplicate(z, &shell->left_add_work)); 1113 PetscCall(MatMultTranspose(A, x, shell->left_add_work)); 1114 PetscCall(VecAXPY(z, 1.0, shell->left_add_work)); 1115 } else { 1116 PetscCall(MatMultTranspose(A, x, z)); 1117 PetscCall(VecAXPY(z, 1.0, y)); 1118 } 1119 PetscFunctionReturn(PETSC_SUCCESS); 1120 } 1121 1122 static PetscErrorCode MatMultHermitianTransposeAdd_Shell(Mat A, Vec x, Vec y, Vec z) 1123 { 1124 Mat_Shell *shell = (Mat_Shell *)A->data; 1125 1126 PetscFunctionBegin; 1127 if (y == z) { 1128 if (!shell->left_add_work) PetscCall(VecDuplicate(z, &shell->left_add_work)); 1129 PetscCall(MatMultHermitianTranspose(A, x, shell->left_add_work)); 1130 PetscCall(VecAXPY(z, 1.0, shell->left_add_work)); 1131 } else { 1132 PetscCall(MatMultHermitianTranspose(A, x, z)); 1133 PetscCall(VecAXPY(z, 1.0, y)); 1134 } 1135 PetscFunctionReturn(PETSC_SUCCESS); 1136 } 1137 1138 /* 1139 diag(left)(vscale*A + diag(dshift) + vshift I)diag(right) 1140 */ 1141 static PetscErrorCode MatGetDiagonal_Shell(Mat A, Vec v) 1142 { 1143 Mat_Shell *shell = (Mat_Shell *)A->data; 1144 1145 PetscFunctionBegin; 1146 if (shell->ops->getdiagonal) { 1147 PetscCall((*shell->ops->getdiagonal)(A, v)); 1148 } else SETERRQ(PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Must provide shell matrix with routine to return diagonal using\nMatShellSetOperation(S,MATOP_GET_DIAGONAL,...)"); 1149 PetscCall(VecScale(v, shell->vscale)); 1150 if (shell->dshift) PetscCall(VecAXPY(v, 1.0, shell->dshift)); 1151 PetscCall(VecShift(v, shell->vshift)); 1152 if (shell->left) PetscCall(VecPointwiseMult(v, v, shell->left)); 1153 if (shell->right) PetscCall(VecPointwiseMult(v, v, shell->right)); 1154 if (shell->zrows) { 1155 PetscCall(VecScatterBegin(shell->zvals_sct_r, shell->zvals, v, INSERT_VALUES, SCATTER_REVERSE)); 1156 PetscCall(VecScatterEnd(shell->zvals_sct_r, shell->zvals, v, INSERT_VALUES, SCATTER_REVERSE)); 1157 } 1158 if (shell->axpy) { 1159 Mat X; 1160 PetscObjectState axpy_state; 1161 1162 PetscCall(MatShellGetContext(shell->axpy, &X)); 1163 PetscCall(PetscObjectStateGet((PetscObject)X, &axpy_state)); 1164 PetscCheck(shell->axpy_state == axpy_state, PetscObjectComm((PetscObject)A), PETSC_ERR_ORDER, "Invalid AXPY state: cannot modify the X matrix passed to MatAXPY(Y,a,X,...)"); 1165 PetscCall(MatCreateVecs(shell->axpy, NULL, shell->axpy_left ? NULL : &shell->axpy_left)); 1166 PetscCall(MatGetDiagonal(shell->axpy, shell->axpy_left)); 1167 PetscCall(VecAXPY(v, shell->axpy_vscale, shell->axpy_left)); 1168 } 1169 PetscFunctionReturn(PETSC_SUCCESS); 1170 } 1171 1172 static PetscErrorCode MatGetDiagonalBlock_Shell(Mat A, Mat *b) 1173 { 1174 Mat_Shell *shell = (Mat_Shell *)A->data; 1175 Vec left = NULL, right = NULL; 1176 1177 PetscFunctionBegin; 1178 PetscCheck(!shell->zrows && !shell->zcols, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Cannot call MatGetDiagonalBlock() if MatZeroRows() or MatZeroRowsColumns() has been called on the input Mat"); // TODO FIXME 1179 PetscCheck(!shell->axpy, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Cannot call MatGetDiagonalBlock() if MatAXPY() has been called on the input Mat"); // TODO FIXME 1180 PetscCheck(!shell->dshift, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Cannot call MatGetDiagonalBlock() if MatDiagonalSet() has been called on the input Mat"); // TODO FIXME 1181 if (shell->ops->getdiagonalblock) { 1182 PetscCall((*shell->ops->getdiagonalblock)(A, b)); 1183 } else SETERRQ(PetscObjectComm((PetscObject)A), PETSC_ERR_ARG_WRONGSTATE, "Must provide shell matrix with routine to return diagonal block using\nMatShellSetOperation(S,MATOP_GET_DIAGONAL_BLOCK,...)"); 1184 PetscCall(MatScale(*b, shell->vscale)); 1185 PetscCall(MatShift(*b, shell->vshift)); 1186 if (shell->left) { 1187 PetscCall(VecCreateLocalVector(shell->left, &left)); 1188 PetscCall(VecGetLocalVectorRead(shell->left, left)); 1189 } 1190 if (shell->right) { 1191 PetscCall(VecCreateLocalVector(shell->right, &right)); 1192 PetscCall(VecGetLocalVectorRead(shell->right, right)); 1193 } 1194 PetscCall(MatDiagonalScale(*b, left, right)); 1195 if (shell->left) { 1196 PetscCall(VecRestoreLocalVectorRead(shell->left, left)); 1197 PetscCall(VecDestroy(&left)); 1198 } 1199 if (shell->right) { 1200 PetscCall(VecRestoreLocalVectorRead(shell->right, right)); 1201 PetscCall(VecDestroy(&right)); 1202 } 1203 PetscFunctionReturn(PETSC_SUCCESS); 1204 } 1205 1206 static PetscErrorCode MatShift_Shell(Mat Y, PetscScalar a) 1207 { 1208 Mat_Shell *shell = (Mat_Shell *)Y->data; 1209 PetscBool flg; 1210 1211 PetscFunctionBegin; 1212 PetscCall(MatHasCongruentLayouts(Y, &flg)); 1213 PetscCheck(flg, PetscObjectComm((PetscObject)Y), PETSC_ERR_SUP, "Cannot shift shell matrix if it is not congruent"); 1214 if (shell->left || shell->right) { 1215 if (!shell->dshift) { 1216 PetscCall(VecDuplicate(shell->left ? shell->left : shell->right, &shell->dshift)); 1217 PetscCall(VecSet(shell->dshift, a)); 1218 } else { 1219 if (shell->left) PetscCall(VecPointwiseMult(shell->dshift, shell->dshift, shell->left)); 1220 if (shell->right) PetscCall(VecPointwiseMult(shell->dshift, shell->dshift, shell->right)); 1221 PetscCall(VecShift(shell->dshift, a)); 1222 } 1223 if (shell->left) PetscCall(VecPointwiseDivide(shell->dshift, shell->dshift, shell->left)); 1224 if (shell->right) PetscCall(VecPointwiseDivide(shell->dshift, shell->dshift, shell->right)); 1225 } else shell->vshift += a; 1226 if (shell->zrows) PetscCall(VecShift(shell->zvals, a)); 1227 PetscFunctionReturn(PETSC_SUCCESS); 1228 } 1229 1230 static PetscErrorCode MatDiagonalSet_Shell_Private(Mat A, Vec D, PetscScalar s) 1231 { 1232 Mat_Shell *shell = (Mat_Shell *)A->data; 1233 1234 PetscFunctionBegin; 1235 if (!shell->dshift) PetscCall(VecDuplicate(D, &shell->dshift)); 1236 if (shell->left || shell->right) { 1237 if (!shell->right_work) PetscCall(VecDuplicate(shell->left ? shell->left : shell->right, &shell->right_work)); 1238 if (shell->left && shell->right) { 1239 PetscCall(VecPointwiseDivide(shell->right_work, D, shell->left)); 1240 PetscCall(VecPointwiseDivide(shell->right_work, shell->right_work, shell->right)); 1241 } else if (shell->left) { 1242 PetscCall(VecPointwiseDivide(shell->right_work, D, shell->left)); 1243 } else { 1244 PetscCall(VecPointwiseDivide(shell->right_work, D, shell->right)); 1245 } 1246 PetscCall(VecAXPY(shell->dshift, s, shell->right_work)); 1247 } else { 1248 PetscCall(VecAXPY(shell->dshift, s, D)); 1249 } 1250 PetscFunctionReturn(PETSC_SUCCESS); 1251 } 1252 1253 static PetscErrorCode MatDiagonalSet_Shell(Mat A, Vec D, InsertMode ins) 1254 { 1255 Mat_Shell *shell = (Mat_Shell *)A->data; 1256 Vec d; 1257 PetscBool flg; 1258 1259 PetscFunctionBegin; 1260 PetscCall(MatHasCongruentLayouts(A, &flg)); 1261 PetscCheck(flg, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Cannot diagonal set or shift shell matrix if it is not congruent"); 1262 if (ins == INSERT_VALUES) { 1263 PetscCall(VecDuplicate(D, &d)); 1264 PetscCall(MatGetDiagonal(A, d)); 1265 PetscCall(MatDiagonalSet_Shell_Private(A, d, -1.)); 1266 PetscCall(MatDiagonalSet_Shell_Private(A, D, 1.)); 1267 PetscCall(VecDestroy(&d)); 1268 if (shell->zrows) PetscCall(VecCopy(D, shell->zvals)); 1269 } else { 1270 PetscCall(MatDiagonalSet_Shell_Private(A, D, 1.)); 1271 if (shell->zrows) PetscCall(VecAXPY(shell->zvals, 1.0, D)); 1272 } 1273 PetscFunctionReturn(PETSC_SUCCESS); 1274 } 1275 1276 static PetscErrorCode MatScale_Shell(Mat Y, PetscScalar a) 1277 { 1278 Mat_Shell *shell = (Mat_Shell *)Y->data; 1279 1280 PetscFunctionBegin; 1281 shell->vscale *= a; 1282 shell->vshift *= a; 1283 if (shell->dshift) PetscCall(VecScale(shell->dshift, a)); 1284 shell->axpy_vscale *= a; 1285 if (shell->zrows) PetscCall(VecScale(shell->zvals, a)); 1286 PetscFunctionReturn(PETSC_SUCCESS); 1287 } 1288 1289 static PetscErrorCode MatDiagonalScale_Shell(Mat Y, Vec left, Vec right) 1290 { 1291 Mat_Shell *shell = (Mat_Shell *)Y->data; 1292 1293 PetscFunctionBegin; 1294 if (left) { 1295 if (!shell->left) { 1296 PetscCall(VecDuplicate(left, &shell->left)); 1297 PetscCall(VecCopy(left, shell->left)); 1298 } else { 1299 PetscCall(VecPointwiseMult(shell->left, shell->left, left)); 1300 } 1301 if (shell->zrows) PetscCall(VecPointwiseMult(shell->zvals, shell->zvals, left)); 1302 } 1303 if (right) { 1304 if (!shell->right) { 1305 PetscCall(VecDuplicate(right, &shell->right)); 1306 PetscCall(VecCopy(right, shell->right)); 1307 } else { 1308 PetscCall(VecPointwiseMult(shell->right, shell->right, right)); 1309 } 1310 if (shell->zrows) { 1311 if (!shell->left_work) PetscCall(MatCreateVecs(Y, NULL, &shell->left_work)); 1312 PetscCall(VecSet(shell->zvals_w, 1.0)); 1313 PetscCall(VecScatterBegin(shell->zvals_sct_c, right, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD)); 1314 PetscCall(VecScatterEnd(shell->zvals_sct_c, right, shell->zvals_w, INSERT_VALUES, SCATTER_FORWARD)); 1315 PetscCall(VecPointwiseMult(shell->zvals, shell->zvals, shell->zvals_w)); 1316 } 1317 } 1318 if (shell->axpy) PetscCall(MatDiagonalScale(shell->axpy, left, right)); 1319 PetscFunctionReturn(PETSC_SUCCESS); 1320 } 1321 1322 PETSC_INTERN PetscErrorCode MatAssemblyEnd_Shell(Mat Y, MatAssemblyType t) 1323 { 1324 Mat_Shell *shell = (Mat_Shell *)Y->data; 1325 1326 PetscFunctionBegin; 1327 if (t == MAT_FINAL_ASSEMBLY) { 1328 shell->vshift = 0.0; 1329 shell->vscale = 1.0; 1330 shell->axpy_vscale = 0.0; 1331 shell->axpy_state = 0; 1332 PetscCall(VecDestroy(&shell->dshift)); 1333 PetscCall(VecDestroy(&shell->left)); 1334 PetscCall(VecDestroy(&shell->right)); 1335 PetscCall(MatDestroy(&shell->axpy)); 1336 PetscCall(VecDestroy(&shell->axpy_left)); 1337 PetscCall(VecDestroy(&shell->axpy_right)); 1338 PetscCall(VecScatterDestroy(&shell->zvals_sct_c)); 1339 PetscCall(VecScatterDestroy(&shell->zvals_sct_r)); 1340 PetscCall(ISDestroy(&shell->zrows)); 1341 PetscCall(ISDestroy(&shell->zcols)); 1342 } 1343 PetscFunctionReturn(PETSC_SUCCESS); 1344 } 1345 1346 static PetscErrorCode MatMissingDiagonal_Shell(Mat A, PetscBool *missing, PetscInt *d) 1347 { 1348 PetscFunctionBegin; 1349 *missing = PETSC_FALSE; 1350 PetscFunctionReturn(PETSC_SUCCESS); 1351 } 1352 1353 static PetscErrorCode MatAXPY_Shell(Mat Y, PetscScalar a, Mat X, MatStructure str) 1354 { 1355 Mat_Shell *shell = (Mat_Shell *)Y->data; 1356 1357 PetscFunctionBegin; 1358 if (X == Y) { 1359 PetscCall(MatScale(Y, 1.0 + a)); 1360 PetscFunctionReturn(PETSC_SUCCESS); 1361 } 1362 if (!shell->axpy) { 1363 PetscCall(MatConvertFrom_Shell(X, MATSHELL, MAT_INITIAL_MATRIX, &shell->axpy)); 1364 shell->axpy_vscale = a; 1365 PetscCall(PetscObjectStateGet((PetscObject)X, &shell->axpy_state)); 1366 } else { 1367 PetscCall(MatAXPY(shell->axpy, a / shell->axpy_vscale, X, str)); 1368 } 1369 PetscFunctionReturn(PETSC_SUCCESS); 1370 } 1371 1372 static struct _MatOps MatOps_Values = {NULL, 1373 NULL, 1374 NULL, 1375 NULL, 1376 /* 4*/ MatMultAdd_Shell, 1377 NULL, 1378 MatMultTransposeAdd_Shell, 1379 NULL, 1380 NULL, 1381 NULL, 1382 /*10*/ NULL, 1383 NULL, 1384 NULL, 1385 NULL, 1386 NULL, 1387 /*15*/ NULL, 1388 NULL, 1389 NULL, 1390 MatDiagonalScale_Shell, 1391 NULL, 1392 /*20*/ NULL, 1393 MatAssemblyEnd_Shell, 1394 NULL, 1395 NULL, 1396 /*24*/ MatZeroRows_Shell, 1397 NULL, 1398 NULL, 1399 NULL, 1400 NULL, 1401 /*29*/ NULL, 1402 NULL, 1403 NULL, 1404 /*32*/ NULL, 1405 NULL, 1406 /*34*/ MatDuplicate_Shell, 1407 NULL, 1408 NULL, 1409 NULL, 1410 NULL, 1411 /*39*/ MatAXPY_Shell, 1412 NULL, 1413 NULL, 1414 NULL, 1415 MatCopy_Shell, 1416 /*44*/ NULL, 1417 MatScale_Shell, 1418 MatShift_Shell, 1419 MatDiagonalSet_Shell, 1420 MatZeroRowsColumns_Shell, 1421 /*49*/ NULL, 1422 NULL, 1423 NULL, 1424 NULL, 1425 NULL, 1426 /*54*/ NULL, 1427 NULL, 1428 NULL, 1429 NULL, 1430 NULL, 1431 /*59*/ NULL, 1432 MatDestroy_Shell, 1433 NULL, 1434 MatConvertFrom_Shell, 1435 NULL, 1436 /*64*/ NULL, 1437 NULL, 1438 NULL, 1439 NULL, 1440 NULL, 1441 /*69*/ NULL, 1442 NULL, 1443 MatConvert_Shell, 1444 NULL, 1445 NULL, 1446 /*74*/ NULL, 1447 NULL, 1448 NULL, 1449 NULL, 1450 NULL, 1451 /*79*/ NULL, 1452 NULL, 1453 NULL, 1454 NULL, 1455 NULL, 1456 /*84*/ NULL, 1457 NULL, 1458 NULL, 1459 NULL, 1460 NULL, 1461 /*89*/ NULL, 1462 NULL, 1463 NULL, 1464 NULL, 1465 NULL, 1466 /*94*/ NULL, 1467 NULL, 1468 NULL, 1469 NULL, 1470 NULL, 1471 /*99*/ NULL, 1472 NULL, 1473 NULL, 1474 NULL, 1475 NULL, 1476 /*104*/ NULL, 1477 NULL, 1478 NULL, 1479 NULL, 1480 NULL, 1481 /*109*/ NULL, 1482 NULL, 1483 NULL, 1484 NULL, 1485 MatMissingDiagonal_Shell, 1486 /*114*/ NULL, 1487 NULL, 1488 NULL, 1489 NULL, 1490 NULL, 1491 /*119*/ NULL, 1492 NULL, 1493 NULL, 1494 MatMultHermitianTransposeAdd_Shell, 1495 NULL, 1496 /*124*/ NULL, 1497 NULL, 1498 NULL, 1499 NULL, 1500 NULL, 1501 /*129*/ NULL, 1502 NULL, 1503 NULL, 1504 NULL, 1505 NULL, 1506 /*134*/ NULL, 1507 NULL, 1508 NULL, 1509 NULL, 1510 NULL, 1511 /*139*/ NULL, 1512 NULL, 1513 NULL, 1514 NULL, 1515 NULL, 1516 /*144*/ NULL, 1517 NULL, 1518 NULL, 1519 NULL, 1520 NULL, 1521 NULL, 1522 /*150*/ NULL, 1523 NULL, 1524 NULL, 1525 NULL, 1526 NULL, 1527 /*155*/ NULL, 1528 NULL}; 1529 1530 static PetscErrorCode MatShellSetContext_Shell(Mat mat, void *ctx) 1531 { 1532 Mat_Shell *shell = (Mat_Shell *)mat->data; 1533 1534 PetscFunctionBegin; 1535 if (ctx) { 1536 PetscContainer ctxcontainer; 1537 PetscCall(PetscContainerCreate(PetscObjectComm((PetscObject)mat), &ctxcontainer)); 1538 PetscCall(PetscContainerSetPointer(ctxcontainer, ctx)); 1539 PetscCall(PetscObjectCompose((PetscObject)mat, "MatShell ctx", (PetscObject)ctxcontainer)); 1540 shell->ctxcontainer = ctxcontainer; 1541 PetscCall(PetscContainerDestroy(&ctxcontainer)); 1542 } else { 1543 PetscCall(PetscObjectCompose((PetscObject)mat, "MatShell ctx", NULL)); 1544 shell->ctxcontainer = NULL; 1545 } 1546 PetscFunctionReturn(PETSC_SUCCESS); 1547 } 1548 1549 static PetscErrorCode MatShellSetContextDestroy_Shell(Mat mat, PetscCtxDestroyFn *f) 1550 { 1551 Mat_Shell *shell = (Mat_Shell *)mat->data; 1552 1553 PetscFunctionBegin; 1554 if (shell->ctxcontainer) PetscCall(PetscContainerSetCtxDestroy(shell->ctxcontainer, f)); 1555 PetscFunctionReturn(PETSC_SUCCESS); 1556 } 1557 1558 PetscErrorCode MatShellSetContext_Immutable(Mat mat, void *ctx) 1559 { 1560 PetscFunctionBegin; 1561 SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Cannot call MatShellSetContext() for a %s, it is used internally by the structure", ((PetscObject)mat)->type_name); 1562 PetscFunctionReturn(PETSC_SUCCESS); 1563 } 1564 1565 PetscErrorCode MatShellSetContextDestroy_Immutable(Mat mat, PetscCtxDestroyFn *f) 1566 { 1567 PetscFunctionBegin; 1568 SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Cannot call MatShellSetContextDestroy() for a %s, it is used internally by the structure", ((PetscObject)mat)->type_name); 1569 PetscFunctionReturn(PETSC_SUCCESS); 1570 } 1571 1572 PetscErrorCode MatShellSetManageScalingShifts_Immutable(Mat mat) 1573 { 1574 PetscFunctionBegin; 1575 SETERRQ(PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "Cannot call MatShellSetManageScalingShifts() for a %s, it is used internally by the structure", ((PetscObject)mat)->type_name); 1576 PetscFunctionReturn(PETSC_SUCCESS); 1577 } 1578 1579 static PetscErrorCode MatShellSetVecType_Shell(Mat mat, VecType vtype) 1580 { 1581 PetscFunctionBegin; 1582 PetscCall(PetscFree(mat->defaultvectype)); 1583 PetscCall(PetscStrallocpy(vtype, (char **)&mat->defaultvectype)); 1584 PetscFunctionReturn(PETSC_SUCCESS); 1585 } 1586 1587 static PetscErrorCode MatShellSetManageScalingShifts_Shell(Mat A) 1588 { 1589 Mat_Shell *shell = (Mat_Shell *)A->data; 1590 1591 PetscFunctionBegin; 1592 shell->managescalingshifts = PETSC_FALSE; 1593 A->ops->diagonalset = NULL; 1594 A->ops->diagonalscale = NULL; 1595 A->ops->scale = NULL; 1596 A->ops->shift = NULL; 1597 A->ops->axpy = NULL; 1598 PetscFunctionReturn(PETSC_SUCCESS); 1599 } 1600 1601 static PetscErrorCode MatShellGetScalingShifts_Shell(Mat A, PetscScalar *vshift, PetscScalar *vscale, Vec *dshift, Vec *left, Vec *right, Mat *axpy, IS *zrows, IS *zcols) 1602 { 1603 Mat_Shell *shell = (Mat_Shell *)A->data; 1604 1605 PetscFunctionBegin; 1606 PetscCheck(shell->managescalingshifts, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Cannot get scaling and shifts if MatShellSetManageScalingShifts() has been called"); 1607 if (vshift == MAT_SHELL_NOT_ALLOWED) PetscCheck(shell->vshift == 0.0, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Non-trivial member in parent MatShell: vshift != 0.0, set via MatShift()"); 1608 else if (vshift) *vshift = shell->vshift; 1609 if (vscale == MAT_SHELL_NOT_ALLOWED) PetscCheck(shell->vscale == 1.0, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Non-trivial member in parent MatShell: vscale != 1.0, set via MatScale()"); 1610 else if (vscale) *vscale = shell->vscale; 1611 if (dshift == MAT_SHELL_NOT_ALLOWED) PetscCheck(!shell->dshift, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Non-trivial member in parent MatShell: dshift, set via MatDiagonalSet()"); 1612 else if (dshift) *dshift = shell->dshift; 1613 if (left == MAT_SHELL_NOT_ALLOWED) PetscCheck(!shell->left, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Non-trivial member in parent MatShell: left, set via MatDiagonalScale()"); 1614 else if (left) *left = shell->left; 1615 if (right == MAT_SHELL_NOT_ALLOWED) PetscCheck(!shell->right, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Non-trivial member in parent MatShell: right, set via MatDiagonalScale()"); 1616 else if (right) *right = shell->right; 1617 if (axpy == MAT_SHELL_NOT_ALLOWED) PetscCheck(!shell->axpy, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Non-trivial member in parent MatShell: axpy, set via MatAXPY()"); 1618 else if (axpy) *axpy = shell->axpy; 1619 if (zrows == MAT_SHELL_NOT_ALLOWED) PetscCheck(!shell->zrows, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Non-trivial member in parent MatShell: zrows, set via MatZeroRows()"); 1620 else if (zrows) *zrows = shell->zrows; 1621 if (zcols == MAT_SHELL_NOT_ALLOWED) PetscCheck(!shell->zcols, PetscObjectComm((PetscObject)A), PETSC_ERR_SUP, "Non-trivial member in parent MatShell: zcols, set via MatZeroRowsColumns()"); 1622 else if (zcols) *zcols = shell->zcols; 1623 PetscFunctionReturn(PETSC_SUCCESS); 1624 } 1625 1626 static PetscErrorCode MatShellSetOperation_Shell(Mat mat, MatOperation op, void (*f)(void)) 1627 { 1628 Mat_Shell *shell = (Mat_Shell *)mat->data; 1629 1630 PetscFunctionBegin; 1631 switch (op) { 1632 case MATOP_DESTROY: 1633 shell->ops->destroy = (PetscErrorCode (*)(Mat))f; 1634 break; 1635 case MATOP_VIEW: 1636 if (!mat->ops->viewnative) mat->ops->viewnative = mat->ops->view; 1637 mat->ops->view = (PetscErrorCode (*)(Mat, PetscViewer))f; 1638 break; 1639 case MATOP_COPY: 1640 shell->ops->copy = (PetscErrorCode (*)(Mat, Mat, MatStructure))f; 1641 break; 1642 case MATOP_DIAGONAL_SET: 1643 case MATOP_DIAGONAL_SCALE: 1644 case MATOP_SHIFT: 1645 case MATOP_SCALE: 1646 case MATOP_AXPY: 1647 case MATOP_ZERO_ROWS: 1648 case MATOP_ZERO_ROWS_COLUMNS: 1649 PetscCheck(!shell->managescalingshifts, PetscObjectComm((PetscObject)mat), PETSC_ERR_ARG_WRONGSTATE, "MATSHELL is managing scalings and shifts, see MatShellSetManageScalingShifts()"); 1650 (((void (**)(void))mat->ops)[op]) = f; 1651 break; 1652 case MATOP_GET_DIAGONAL: 1653 if (shell->managescalingshifts) { 1654 shell->ops->getdiagonal = (PetscErrorCode (*)(Mat, Vec))f; 1655 mat->ops->getdiagonal = MatGetDiagonal_Shell; 1656 } else { 1657 shell->ops->getdiagonal = NULL; 1658 mat->ops->getdiagonal = (PetscErrorCode (*)(Mat, Vec))f; 1659 } 1660 break; 1661 case MATOP_GET_DIAGONAL_BLOCK: 1662 if (shell->managescalingshifts) { 1663 shell->ops->getdiagonalblock = (PetscErrorCode (*)(Mat, Mat *))f; 1664 mat->ops->getdiagonalblock = MatGetDiagonalBlock_Shell; 1665 } else { 1666 shell->ops->getdiagonalblock = NULL; 1667 mat->ops->getdiagonalblock = (PetscErrorCode (*)(Mat, Mat *))f; 1668 } 1669 break; 1670 case MATOP_MULT: 1671 if (shell->managescalingshifts) { 1672 shell->ops->mult = (PetscErrorCode (*)(Mat, Vec, Vec))f; 1673 mat->ops->mult = MatMult_Shell; 1674 } else { 1675 shell->ops->mult = NULL; 1676 mat->ops->mult = (PetscErrorCode (*)(Mat, Vec, Vec))f; 1677 } 1678 break; 1679 case MATOP_MULT_TRANSPOSE: 1680 if (shell->managescalingshifts) { 1681 shell->ops->multtranspose = (PetscErrorCode (*)(Mat, Vec, Vec))f; 1682 mat->ops->multtranspose = MatMultTranspose_Shell; 1683 } else { 1684 shell->ops->multtranspose = NULL; 1685 mat->ops->multtranspose = (PetscErrorCode (*)(Mat, Vec, Vec))f; 1686 } 1687 break; 1688 case MATOP_MULT_HERMITIAN_TRANSPOSE: 1689 if (shell->managescalingshifts) { 1690 shell->ops->multhermitiantranspose = (PetscErrorCode (*)(Mat, Vec, Vec))f; 1691 mat->ops->multhermitiantranspose = MatMultHermitianTranspose_Shell; 1692 } else { 1693 shell->ops->multhermitiantranspose = NULL; 1694 mat->ops->multhermitiantranspose = (PetscErrorCode (*)(Mat, Vec, Vec))f; 1695 } 1696 break; 1697 default: 1698 (((void (**)(void))mat->ops)[op]) = f; 1699 break; 1700 } 1701 PetscFunctionReturn(PETSC_SUCCESS); 1702 } 1703 1704 static PetscErrorCode MatShellGetOperation_Shell(Mat mat, MatOperation op, void (**f)(void)) 1705 { 1706 Mat_Shell *shell = (Mat_Shell *)mat->data; 1707 1708 PetscFunctionBegin; 1709 switch (op) { 1710 case MATOP_DESTROY: 1711 *f = (void (*)(void))shell->ops->destroy; 1712 break; 1713 case MATOP_VIEW: 1714 *f = (void (*)(void))mat->ops->view; 1715 break; 1716 case MATOP_COPY: 1717 *f = (void (*)(void))shell->ops->copy; 1718 break; 1719 case MATOP_DIAGONAL_SET: 1720 case MATOP_DIAGONAL_SCALE: 1721 case MATOP_SHIFT: 1722 case MATOP_SCALE: 1723 case MATOP_AXPY: 1724 case MATOP_ZERO_ROWS: 1725 case MATOP_ZERO_ROWS_COLUMNS: 1726 *f = (((void (**)(void))mat->ops)[op]); 1727 break; 1728 case MATOP_GET_DIAGONAL: 1729 if (shell->ops->getdiagonal) *f = (void (*)(void))shell->ops->getdiagonal; 1730 else *f = (((void (**)(void))mat->ops)[op]); 1731 break; 1732 case MATOP_GET_DIAGONAL_BLOCK: 1733 if (shell->ops->getdiagonalblock) *f = (void (*)(void))shell->ops->getdiagonalblock; 1734 else *f = (((void (**)(void))mat->ops)[op]); 1735 break; 1736 case MATOP_MULT: 1737 if (shell->ops->mult) *f = (void (*)(void))shell->ops->mult; 1738 else *f = (((void (**)(void))mat->ops)[op]); 1739 break; 1740 case MATOP_MULT_TRANSPOSE: 1741 if (shell->ops->multtranspose) *f = (void (*)(void))shell->ops->multtranspose; 1742 else *f = (((void (**)(void))mat->ops)[op]); 1743 break; 1744 case MATOP_MULT_HERMITIAN_TRANSPOSE: 1745 if (shell->ops->multhermitiantranspose) *f = (void (*)(void))shell->ops->multhermitiantranspose; 1746 else *f = (((void (**)(void))mat->ops)[op]); 1747 break; 1748 default: 1749 *f = (((void (**)(void))mat->ops)[op]); 1750 } 1751 PetscFunctionReturn(PETSC_SUCCESS); 1752 } 1753 1754 /*MC 1755 MATSHELL - MATSHELL = "shell" - A matrix type to be used to define your own matrix type with its own data structure -- perhaps matrix-free. 1756 1757 Level: advanced 1758 1759 Notes: 1760 See `MatCreateShell()` for details on the usage of `MATSHELL` 1761 1762 `PCSHELL` can be used in conjunction with `MATSHELL` to provide a custom preconditioner appropriate for your `MATSHELL`. Since 1763 many standard preconditioners such as `PCILU` depend on having an explicit representation of the matrix entries they cannot be used 1764 directly with `MATSHELL`. 1765 1766 .seealso: [](ch_matrices), `Mat`, `MatCreateShell()`, `PCSHELL` 1767 M*/ 1768 1769 PETSC_EXTERN PetscErrorCode MatCreate_Shell(Mat A) 1770 { 1771 Mat_Shell *b; 1772 1773 PetscFunctionBegin; 1774 PetscCall(PetscNew(&b)); 1775 A->data = (void *)b; 1776 A->ops[0] = MatOps_Values; 1777 1778 b->ctxcontainer = NULL; 1779 b->vshift = 0.0; 1780 b->vscale = 1.0; 1781 b->managescalingshifts = PETSC_TRUE; 1782 A->assembled = PETSC_TRUE; 1783 A->preallocated = PETSC_FALSE; 1784 1785 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellGetContext_C", MatShellGetContext_Shell)); 1786 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContext_C", MatShellSetContext_Shell)); 1787 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetContextDestroy_C", MatShellSetContextDestroy_Shell)); 1788 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetVecType_C", MatShellSetVecType_Shell)); 1789 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetManageScalingShifts_C", MatShellSetManageScalingShifts_Shell)); 1790 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellGetScalingShifts_C", MatShellGetScalingShifts_Shell)); 1791 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetOperation_C", MatShellSetOperation_Shell)); 1792 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellGetOperation_C", MatShellGetOperation_Shell)); 1793 PetscCall(PetscObjectComposeFunction((PetscObject)A, "MatShellSetMatProductOperation_C", MatShellSetMatProductOperation_Shell)); 1794 PetscCall(PetscObjectChangeTypeName((PetscObject)A, MATSHELL)); 1795 PetscFunctionReturn(PETSC_SUCCESS); 1796 } 1797 1798 /*@C 1799 MatCreateShell - Creates a new matrix of `MatType` `MATSHELL` for use with a user-defined 1800 private matrix data storage format. 1801 1802 Collective 1803 1804 Input Parameters: 1805 + comm - MPI communicator 1806 . m - number of local rows (or `PETSC_DECIDE` to have calculated if `M` is given) 1807 . n - number of local columns (or `PETSC_DECIDE` to have calculated if `N` is given) 1808 . M - number of global rows (may be `PETSC_DETERMINE` to have calculated if `m` is given) 1809 . N - number of global columns (may be `PETSC_DETERMINE` to have calculated if `n` is given) 1810 - ctx - pointer to data needed by the shell matrix routines 1811 1812 Output Parameter: 1813 . A - the matrix 1814 1815 Level: advanced 1816 1817 Example Usage: 1818 .vb 1819 extern PetscErrorCode mult(Mat, Vec, Vec); 1820 1821 MatCreateShell(comm, m, n, M, N, ctx, &mat); 1822 MatShellSetOperation(mat, MATOP_MULT, (void(*)(void))mult); 1823 MatShellSetContext(mat,ctx); 1824 // Use matrix for operations that have been set 1825 MatDestroy(mat); 1826 .ve 1827 1828 Notes: 1829 The shell matrix type is intended to provide a simple way for users to write a custom matrix specifically for their application. 1830 1831 `MatCreateShell()` is used in conjunction with `MatShellSetContext()` and `MatShellSetOperation()`. 1832 1833 PETSc requires that matrices and vectors being used for certain 1834 operations are partitioned accordingly. For example, when 1835 creating a shell matrix, `A`, that supports parallel matrix-vector 1836 products using `MatMult`(A,x,y) the user should set the number 1837 of local matrix rows to be the number of local elements of the 1838 corresponding result vector, y. Note that this is information is 1839 required for use of the matrix interface routines, even though 1840 the shell matrix may not actually be physically partitioned. 1841 For example, 1842 1843 .vb 1844 Vec x, y 1845 extern PetscErrorCode mult(Mat,Vec,Vec); 1846 Mat A 1847 1848 VecCreateMPI(comm,PETSC_DECIDE,M,&y); 1849 VecCreateMPI(comm,PETSC_DECIDE,N,&x); 1850 VecGetLocalSize(y,&m); 1851 VecGetLocalSize(x,&n); 1852 MatCreateShell(comm,m,n,M,N,ctx,&A); 1853 MatShellSetOperation(mat,MATOP_MULT,(void(*)(void))mult); 1854 MatMult(A,x,y); 1855 MatDestroy(&A); 1856 VecDestroy(&y); 1857 VecDestroy(&x); 1858 .ve 1859 1860 `MATSHELL` handles `MatShift()`, `MatDiagonalSet()`, `MatDiagonalScale()`, `MatAXPY()`, `MatScale()`, `MatZeroRows()` and `MatZeroRowsColumns()` 1861 internally, so these operations cannot be overwritten unless `MatShellSetManageScalingShifts()` is called. 1862 1863 Developer Notes: 1864 For rectangular matrices do all the scalings and shifts make sense? 1865 1866 Regarding shifting and scaling. The general form is 1867 1868 diag(left)(vscale*A + diag(dshift) + vshift I)diag(right) 1869 1870 The order you apply the operations is important. For example if you have a dshift then 1871 apply a MatScale(s) you get s*vscale*A + s*diag(shift). But if you first scale and then shift 1872 you get s*vscale*A + diag(shift) 1873 1874 A is the user provided function. 1875 1876 `KSP`/`PC` uses changes in the `Mat`'s "state" to decide if preconditioners need to be rebuilt `PCSetUp()` only calls the setup() for 1877 for the `PC` implementation if the `Mat` state has increased from the previous call. Thus to get changes in a `MATSHELL` to trigger 1878 an update in the preconditioner you must call `MatAssemblyBegin()` and `MatAssemblyEnd()` or `PetscObjectStateIncrease`((`PetscObject`)mat); 1879 each time the `MATSHELL` matrix has changed. 1880 1881 Matrix-matrix product operations can be specified using `MatShellSetMatProductOperation()` 1882 1883 Calling `MatAssemblyBegin()`/`MatAssemblyEnd()` on a `MATSHELL` removes any previously supplied shift and scales that were provided 1884 with `MatDiagonalSet()`, `MatShift()`, `MatScale()`, or `MatDiagonalScale()`. 1885 1886 Fortran Notes: 1887 To use this from Fortran with a `ctx` you must write an interface definition for this 1888 function and for `MatShellGetContext()` that tells Fortran the Fortran derived data type you are passing 1889 in as the `ctx` argument. 1890 1891 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatShellSetOperation()`, `MatHasOperation()`, `MatShellGetContext()`, `MatShellSetContext()`, `MatShellSetManageScalingShifts()`, `MatShellSetMatProductOperation()` 1892 @*/ 1893 PetscErrorCode MatCreateShell(MPI_Comm comm, PetscInt m, PetscInt n, PetscInt M, PetscInt N, void *ctx, Mat *A) 1894 { 1895 PetscFunctionBegin; 1896 PetscCall(MatCreate(comm, A)); 1897 PetscCall(MatSetSizes(*A, m, n, M, N)); 1898 PetscCall(MatSetType(*A, MATSHELL)); 1899 PetscCall(MatShellSetContext(*A, ctx)); 1900 PetscCall(MatSetUp(*A)); 1901 PetscFunctionReturn(PETSC_SUCCESS); 1902 } 1903 1904 /*@ 1905 MatShellSetContext - sets the context for a `MATSHELL` shell matrix 1906 1907 Logically Collective 1908 1909 Input Parameters: 1910 + mat - the `MATSHELL` shell matrix 1911 - ctx - the context 1912 1913 Level: advanced 1914 1915 Note: 1916 This provides an easy way, along with `MatCreateShell()` and `MatShellSetOperation()` to provide a custom matrix format 1917 specifically for your application. 1918 1919 Fortran Notes: 1920 You must write a Fortran interface definition for this 1921 function that tells Fortran the Fortran derived data type that you are passing in as the `ctx` argument. 1922 1923 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()` 1924 @*/ 1925 PetscErrorCode MatShellSetContext(Mat mat, void *ctx) 1926 { 1927 PetscFunctionBegin; 1928 PetscValidHeaderSpecific(mat, MAT_CLASSID, 1); 1929 PetscTryMethod(mat, "MatShellSetContext_C", (Mat, void *), (mat, ctx)); 1930 PetscFunctionReturn(PETSC_SUCCESS); 1931 } 1932 1933 /*@C 1934 MatShellSetContextDestroy - sets the destroy function for a `MATSHELL` shell matrix context 1935 1936 Logically Collective 1937 1938 Input Parameters: 1939 + mat - the shell matrix 1940 - f - the context destroy function, see `PetscCtxDestroyFn` for calling sequence 1941 1942 Level: advanced 1943 1944 Note: 1945 If the `MatShell` is never duplicated, the behavior of this function is equivalent 1946 to `MatShellSetOperation`(`Mat`,`MATOP_DESTROY`,f). However, `MatShellSetContextDestroy()` 1947 ensures proper reference counting for the user provided context data in the case that 1948 the `MATSHELL` is duplicated. 1949 1950 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellSetContext()`, 1951 `PetscCtxDestroyFn` 1952 @*/ 1953 PetscErrorCode MatShellSetContextDestroy(Mat mat, PetscCtxDestroyFn *f) 1954 { 1955 PetscFunctionBegin; 1956 PetscValidHeaderSpecific(mat, MAT_CLASSID, 1); 1957 PetscTryMethod(mat, "MatShellSetContextDestroy_C", (Mat, PetscCtxDestroyFn *), (mat, f)); 1958 PetscFunctionReturn(PETSC_SUCCESS); 1959 } 1960 1961 /*@C 1962 MatShellSetVecType - Sets the `VecType` of `Vec` returned by `MatCreateVecs()` 1963 1964 Logically Collective 1965 1966 Input Parameters: 1967 + mat - the `MATSHELL` shell matrix 1968 - vtype - type to use for creating vectors 1969 1970 Level: advanced 1971 1972 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateVecs()` 1973 @*/ 1974 PetscErrorCode MatShellSetVecType(Mat mat, VecType vtype) 1975 { 1976 PetscFunctionBegin; 1977 PetscTryMethod(mat, "MatShellSetVecType_C", (Mat, VecType), (mat, vtype)); 1978 PetscFunctionReturn(PETSC_SUCCESS); 1979 } 1980 1981 /*@ 1982 MatShellSetManageScalingShifts - Allows the user to control the scaling and shift operations of the `MATSHELL`. Must be called immediately 1983 after `MatCreateShell()` 1984 1985 Logically Collective 1986 1987 Input Parameter: 1988 . A - the `MATSHELL` shell matrix 1989 1990 Level: advanced 1991 1992 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellSetContext()`, `MatShellSetOperation()` 1993 @*/ 1994 PetscErrorCode MatShellSetManageScalingShifts(Mat A) 1995 { 1996 PetscFunctionBegin; 1997 PetscValidHeaderSpecific(A, MAT_CLASSID, 1); 1998 PetscTryMethod(A, "MatShellSetManageScalingShifts_C", (Mat), (A)); 1999 PetscFunctionReturn(PETSC_SUCCESS); 2000 } 2001 2002 /*@C 2003 MatShellGetScalingShifts - Gets members of a `MATSHELL` used internally for scaling and 2004 shifting the `Mat` or calling `MatAXPY()`, `MatZeroRows()`, or `MatZeroRowsColumns()` with it 2005 2006 Logically Collective 2007 2008 Input Parameter: 2009 . A - the `MATSHELL` shell matrix 2010 2011 Output Parameters: 2012 + vshift - `PetscScalar` pointer (can be `NULL`), see `MatShift()`, or `MAT_SHELL_NOT_ALLOWED` if the internal shift should be 0 2013 . vscale - `PetscScalar` pointer (can be `NULL`), see `MatScale()`, or `MAT_SHELL_NOT_ALLOWED` if the internal scaling should be 1 2014 . dshift - `Vec` pointer (can be `NULL`), see `MatDiagonalSet()`, or `MAT_SHELL_NOT_ALLOWED` if the internal shift should be `NULL` 2015 . left - `Vec` pointer (can be `NULL`), see `MatDiagonalScale()`, or `MAT_SHELL_NOT_ALLOWED` if the internal scaling should be `NULL` 2016 . right - `Vec` pointer (can be `NULL`), see `MatDiagonalScale()`, or `MAT_SHELL_NOT_ALLOWED` if the internal scaling should be `NULL` 2017 . axpy - `Mat` pointer (can be `NULL`), or `MAT_SHELL_NOT_ALLOWED` if `MatAXPY()` should have not been called on `A` 2018 . zrows - `Vec` pointer (can be `NULL`), or `MAT_SHELL_NOT_ALLOWED` if `MatZeroRows()` should have not been called on `A` 2019 - zcols - `Vec` pointer (can be `NULL`), or `MAT_SHELL_NOT_ALLOWED` if `MatZeroRowsColumns()` should have not been called on `A` 2020 2021 Level: advanced 2022 2023 Developer Notes: 2024 This is mostly useful to check for corner-cases in `MatType` deriving from 2025 `MATSHELL`, e.g, `MATCOMPOSITE` or `MATTRANSPOSEVIRTUAL`, since scaling and 2026 shifts often require extra work which is not always implemented. 2027 2028 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShift()`, `MatScale()`, `MatDiagonalSet()`, `MatDiagonalScale()`, `MatAXPY()`, `MatZeroRows()`, `MatZeroRowsColumns()`, `MatShellSetManageScalingShifts()` 2029 @*/ 2030 PetscErrorCode MatShellGetScalingShifts(Mat A, PetscScalar *vshift, PetscScalar *vscale, Vec *dshift, Vec *left, Vec *right, Mat *axpy, IS *zrows, IS *zcols) 2031 { 2032 PetscFunctionBegin; 2033 PetscValidHeaderSpecific(A, MAT_CLASSID, 1); 2034 PetscTryMethod(A, "MatShellGetScalingShifts_C", (Mat, PetscScalar *, PetscScalar *, Vec *, Vec *, Vec *, Mat *, IS *, IS *), (A, vshift, vscale, dshift, left, right, axpy, zrows, zcols)); 2035 PetscFunctionReturn(PETSC_SUCCESS); 2036 } 2037 2038 /*@C 2039 MatShellTestMult - Compares the multiply routine provided to the `MATSHELL` with differencing on a given function. 2040 2041 Logically Collective; No Fortran Support 2042 2043 Input Parameters: 2044 + mat - the `MATSHELL` shell matrix 2045 . f - the function 2046 . base - differences are computed around this vector, see `MatMFFDSetBase()`, for Jacobians this is the point at which the Jacobian is being evaluated 2047 - ctx - an optional context for the function 2048 2049 Output Parameter: 2050 . flg - `PETSC_TRUE` if the multiply is likely correct 2051 2052 Options Database Key: 2053 . -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference 2054 2055 Level: advanced 2056 2057 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellTestMultTranspose()` 2058 @*/ 2059 PetscErrorCode MatShellTestMult(Mat mat, PetscErrorCode (*f)(void *, Vec, Vec), Vec base, void *ctx, PetscBool *flg) 2060 { 2061 PetscInt m, n; 2062 Mat mf, Dmf, Dmat, Ddiff; 2063 PetscReal Diffnorm, Dmfnorm; 2064 PetscBool v = PETSC_FALSE, flag = PETSC_TRUE; 2065 2066 PetscFunctionBegin; 2067 PetscValidHeaderSpecific(mat, MAT_CLASSID, 1); 2068 PetscCall(PetscOptionsHasName(NULL, ((PetscObject)mat)->prefix, "-mat_shell_test_mult_view", &v)); 2069 PetscCall(MatGetLocalSize(mat, &m, &n)); 2070 PetscCall(MatCreateMFFD(PetscObjectComm((PetscObject)mat), m, n, PETSC_DECIDE, PETSC_DECIDE, &mf)); 2071 PetscCall(MatMFFDSetFunction(mf, f, ctx)); 2072 PetscCall(MatMFFDSetBase(mf, base, NULL)); 2073 2074 PetscCall(MatComputeOperator(mf, MATAIJ, &Dmf)); 2075 PetscCall(MatComputeOperator(mat, MATAIJ, &Dmat)); 2076 2077 PetscCall(MatDuplicate(Dmat, MAT_COPY_VALUES, &Ddiff)); 2078 PetscCall(MatAXPY(Ddiff, -1.0, Dmf, DIFFERENT_NONZERO_PATTERN)); 2079 PetscCall(MatNorm(Ddiff, NORM_FROBENIUS, &Diffnorm)); 2080 PetscCall(MatNorm(Dmf, NORM_FROBENIUS, &Dmfnorm)); 2081 if (Diffnorm / Dmfnorm > 10 * PETSC_SQRT_MACHINE_EPSILON) { 2082 flag = PETSC_FALSE; 2083 if (v) { 2084 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)mat), "MATSHELL and matrix-free multiple appear to produce different results.\n Norm Ratio %g Difference results followed by finite difference one\n", (double)(Diffnorm / Dmfnorm))); 2085 PetscCall(MatViewFromOptions(Ddiff, (PetscObject)mat, "-mat_shell_test_mult_view")); 2086 PetscCall(MatViewFromOptions(Dmf, (PetscObject)mat, "-mat_shell_test_mult_view")); 2087 PetscCall(MatViewFromOptions(Dmat, (PetscObject)mat, "-mat_shell_test_mult_view")); 2088 } 2089 } else if (v) { 2090 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)mat), "MATSHELL and matrix-free multiple appear to produce the same results\n")); 2091 } 2092 if (flg) *flg = flag; 2093 PetscCall(MatDestroy(&Ddiff)); 2094 PetscCall(MatDestroy(&mf)); 2095 PetscCall(MatDestroy(&Dmf)); 2096 PetscCall(MatDestroy(&Dmat)); 2097 PetscFunctionReturn(PETSC_SUCCESS); 2098 } 2099 2100 /*@C 2101 MatShellTestMultTranspose - Compares the multiply transpose routine provided to the `MATSHELL` with differencing on a given function. 2102 2103 Logically Collective; No Fortran Support 2104 2105 Input Parameters: 2106 + mat - the `MATSHELL` shell matrix 2107 . f - the function 2108 . base - differences are computed around this vector, see `MatMFFDSetBase()`, for Jacobians this is the point at which the Jacobian is being evaluated 2109 - ctx - an optional context for the function 2110 2111 Output Parameter: 2112 . flg - `PETSC_TRUE` if the multiply is likely correct 2113 2114 Options Database Key: 2115 . -mat_shell_test_mult_view - print if any differences are detected between the products and print the difference 2116 2117 Level: advanced 2118 2119 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellTestMult()` 2120 @*/ 2121 PetscErrorCode MatShellTestMultTranspose(Mat mat, PetscErrorCode (*f)(void *, Vec, Vec), Vec base, void *ctx, PetscBool *flg) 2122 { 2123 Vec x, y, z; 2124 PetscInt m, n, M, N; 2125 Mat mf, Dmf, Dmat, Ddiff; 2126 PetscReal Diffnorm, Dmfnorm; 2127 PetscBool v = PETSC_FALSE, flag = PETSC_TRUE; 2128 2129 PetscFunctionBegin; 2130 PetscValidHeaderSpecific(mat, MAT_CLASSID, 1); 2131 PetscCall(PetscOptionsHasName(NULL, ((PetscObject)mat)->prefix, "-mat_shell_test_mult_transpose_view", &v)); 2132 PetscCall(MatCreateVecs(mat, &x, &y)); 2133 PetscCall(VecDuplicate(y, &z)); 2134 PetscCall(MatGetLocalSize(mat, &m, &n)); 2135 PetscCall(MatGetSize(mat, &M, &N)); 2136 PetscCall(MatCreateMFFD(PetscObjectComm((PetscObject)mat), m, n, M, N, &mf)); 2137 PetscCall(MatMFFDSetFunction(mf, f, ctx)); 2138 PetscCall(MatMFFDSetBase(mf, base, NULL)); 2139 PetscCall(MatComputeOperator(mf, MATAIJ, &Dmf)); 2140 PetscCall(MatTranspose(Dmf, MAT_INPLACE_MATRIX, &Dmf)); 2141 PetscCall(MatComputeOperatorTranspose(mat, MATAIJ, &Dmat)); 2142 2143 PetscCall(MatDuplicate(Dmat, MAT_COPY_VALUES, &Ddiff)); 2144 PetscCall(MatAXPY(Ddiff, -1.0, Dmf, DIFFERENT_NONZERO_PATTERN)); 2145 PetscCall(MatNorm(Ddiff, NORM_FROBENIUS, &Diffnorm)); 2146 PetscCall(MatNorm(Dmf, NORM_FROBENIUS, &Dmfnorm)); 2147 if (Diffnorm / Dmfnorm > 10 * PETSC_SQRT_MACHINE_EPSILON) { 2148 flag = PETSC_FALSE; 2149 if (v) { 2150 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)mat), "MATSHELL and matrix-free multiple appear to produce different results.\n Norm Ratio %g Difference results followed by finite difference one\n", (double)(Diffnorm / Dmfnorm))); 2151 PetscCall(MatViewFromOptions(Ddiff, (PetscObject)mat, "-mat_shell_test_mult_transpose_view")); 2152 PetscCall(MatViewFromOptions(Dmf, (PetscObject)mat, "-mat_shell_test_mult_transpose_view")); 2153 PetscCall(MatViewFromOptions(Dmat, (PetscObject)mat, "-mat_shell_test_mult_transpose_view")); 2154 } 2155 } else if (v) { 2156 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)mat), "MATSHELL transpose and matrix-free multiple appear to produce the same results\n")); 2157 } 2158 if (flg) *flg = flag; 2159 PetscCall(MatDestroy(&mf)); 2160 PetscCall(MatDestroy(&Dmat)); 2161 PetscCall(MatDestroy(&Ddiff)); 2162 PetscCall(MatDestroy(&Dmf)); 2163 PetscCall(VecDestroy(&x)); 2164 PetscCall(VecDestroy(&y)); 2165 PetscCall(VecDestroy(&z)); 2166 PetscFunctionReturn(PETSC_SUCCESS); 2167 } 2168 2169 /*@C 2170 MatShellSetOperation - Allows user to set a matrix operation for a `MATSHELL` shell matrix. 2171 2172 Logically Collective 2173 2174 Input Parameters: 2175 + mat - the `MATSHELL` shell matrix 2176 . op - the name of the operation 2177 - g - the function that provides the operation. 2178 2179 Level: advanced 2180 2181 Example Usage: 2182 .vb 2183 extern PetscErrorCode usermult(Mat, Vec, Vec); 2184 2185 MatCreateShell(comm, m, n, M, N, ctx, &A); 2186 MatShellSetOperation(A, MATOP_MULT, (void(*)(void))usermult); 2187 .ve 2188 2189 Notes: 2190 See the file include/petscmat.h for a complete list of matrix 2191 operations, which all have the form MATOP_<OPERATION>, where 2192 <OPERATION> is the name (in all capital letters) of the 2193 user interface routine (e.g., `MatMult()` -> `MATOP_MULT`). 2194 2195 All user-provided functions (except for `MATOP_DESTROY`) should have the same calling 2196 sequence as the usual matrix interface routines, since they 2197 are intended to be accessed via the usual matrix interface 2198 routines, e.g., 2199 $ MatMult(Mat, Vec, Vec) -> usermult(Mat, Vec, Vec) 2200 2201 In particular each function MUST return an error code of 0 on success and 2202 nonzero on failure. 2203 2204 Within each user-defined routine, the user should call 2205 `MatShellGetContext()` to obtain the user-defined context that was 2206 set by `MatCreateShell()`. 2207 2208 Use `MatSetOperation()` to set an operation for any matrix type. For matrix product operations (i.e. `MatMatXXX()`, `MatTransposeMatXXX()` etc) 2209 use `MatShellSetMatProductOperation()` 2210 2211 Fortran Note: 2212 For `MatCreateVecs()` the user code should check if the input left or right matrix is -1 and in that case not 2213 generate a matrix. See src/mat/tests/ex120f.F 2214 2215 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellGetOperation()`, `MatShellSetContext()`, `MatSetOperation()`, `MatShellSetManageScalingShifts()`, `MatShellSetMatProductOperation()` 2216 @*/ 2217 PetscErrorCode MatShellSetOperation(Mat mat, MatOperation op, void (*g)(void)) 2218 { 2219 PetscFunctionBegin; 2220 PetscValidHeaderSpecific(mat, MAT_CLASSID, 1); 2221 PetscTryMethod(mat, "MatShellSetOperation_C", (Mat, MatOperation, void (*)(void)), (mat, op, g)); 2222 PetscFunctionReturn(PETSC_SUCCESS); 2223 } 2224 2225 /*@C 2226 MatShellGetOperation - Gets a matrix function for a `MATSHELL` shell matrix. 2227 2228 Not Collective 2229 2230 Input Parameters: 2231 + mat - the `MATSHELL` shell matrix 2232 - op - the name of the operation 2233 2234 Output Parameter: 2235 . g - the function that provides the operation. 2236 2237 Level: advanced 2238 2239 Notes: 2240 See the file include/petscmat.h for a complete list of matrix 2241 operations, which all have the form MATOP_<OPERATION>, where 2242 <OPERATION> is the name (in all capital letters) of the 2243 user interface routine (e.g., `MatMult()` -> `MATOP_MULT`). 2244 2245 All user-provided functions have the same calling 2246 sequence as the usual matrix interface routines, since they 2247 are intended to be accessed via the usual matrix interface 2248 routines, e.g., 2249 $ MatMult(Mat, Vec, Vec) -> usermult(Mat, Vec, Vec) 2250 2251 Within each user-defined routine, the user should call 2252 `MatShellGetContext()` to obtain the user-defined context that was 2253 set by `MatCreateShell()`. 2254 2255 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MatCreateShell()`, `MatShellGetContext()`, `MatShellSetOperation()`, `MatShellSetContext()` 2256 @*/ 2257 PetscErrorCode MatShellGetOperation(Mat mat, MatOperation op, void (**g)(void)) 2258 { 2259 PetscFunctionBegin; 2260 PetscValidHeaderSpecific(mat, MAT_CLASSID, 1); 2261 PetscUseMethod(mat, "MatShellGetOperation_C", (Mat, MatOperation, void (**)(void)), (mat, op, g)); 2262 PetscFunctionReturn(PETSC_SUCCESS); 2263 } 2264 2265 /*@ 2266 MatIsShell - Inquires if a matrix is derived from `MATSHELL` 2267 2268 Input Parameter: 2269 . mat - the matrix 2270 2271 Output Parameter: 2272 . flg - the Boolean value 2273 2274 Level: developer 2275 2276 .seealso: [](ch_matrices), `Mat`, `MATSHELL`, `MATMFFD`, `MatCreateShell()`, `MATTRANSPOSEVIRTUAL`, `MATSCHURCOMPLEMENT` 2277 @*/ 2278 PetscErrorCode MatIsShell(Mat mat, PetscBool *flg) 2279 { 2280 PetscFunctionBegin; 2281 PetscValidHeaderSpecific(mat, MAT_CLASSID, 1); 2282 PetscAssertPointer(flg, 2); 2283 *flg = (PetscBool)(mat->ops->destroy == MatDestroy_Shell); 2284 PetscFunctionReturn(PETSC_SUCCESS); 2285 } 2286