1 #include <petsc/private/dmpleximpl.h> /*I "petscdmplex.h" I*/ 2 #include <petscsf.h> 3 4 #include <petscblaslapack.h> 5 #include <petsc/private/hashsetij.h> 6 #include <petsc/private/petscfeimpl.h> 7 #include <petsc/private/petscfvimpl.h> 8 9 PetscBool Clementcite = PETSC_FALSE; 10 const char ClementCitation[] = "@article{clement1975approximation,\n" 11 " title = {Approximation by finite element functions using local regularization},\n" 12 " author = {Philippe Cl{\\'e}ment},\n" 13 " journal = {Revue fran{\\c{c}}aise d'automatique, informatique, recherche op{\\'e}rationnelle. Analyse num{\\'e}rique},\n" 14 " volume = {9},\n" 15 " number = {R2},\n" 16 " pages = {77--84},\n" 17 " year = {1975}\n}\n"; 18 19 static PetscErrorCode DMPlexConvertPlex(DM dm, DM *plex, PetscBool copy) 20 { 21 PetscBool isPlex; 22 23 PetscFunctionBegin; 24 PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex)); 25 if (isPlex) { 26 *plex = dm; 27 PetscCall(PetscObjectReference((PetscObject)dm)); 28 } else { 29 PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex)); 30 if (!*plex) { 31 PetscCall(DMConvert(dm, DMPLEX, plex)); 32 PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex)); 33 if (copy) { 34 DMSubDomainHookLink link; 35 36 PetscCall(DMCopyAuxiliaryVec(dm, *plex)); 37 /* Run the subdomain hook (this will copy the DMSNES/DMTS) */ 38 for (link = dm->subdomainhook; link; link = link->next) { 39 if (link->ddhook) PetscCall((*link->ddhook)(dm, *plex, link->ctx)); 40 } 41 } 42 } else { 43 PetscCall(PetscObjectReference((PetscObject)*plex)); 44 } 45 } 46 PetscFunctionReturn(PETSC_SUCCESS); 47 } 48 49 static PetscErrorCode PetscContainerUserDestroy_PetscFEGeom(void *ctx) 50 { 51 PetscFEGeom *geom = (PetscFEGeom *)ctx; 52 53 PetscFunctionBegin; 54 PetscCall(PetscFEGeomDestroy(&geom)); 55 PetscFunctionReturn(PETSC_SUCCESS); 56 } 57 58 static PetscErrorCode DMPlexGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom) 59 { 60 char composeStr[33] = {0}; 61 PetscObjectId id; 62 PetscContainer container; 63 64 PetscFunctionBegin; 65 PetscCall(PetscObjectGetId((PetscObject)quad, &id)); 66 PetscCall(PetscSNPrintf(composeStr, 32, "DMPlexGetFEGeom_%" PetscInt64_FMT "\n", id)); 67 PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container)); 68 if (container) { 69 PetscCall(PetscContainerGetPointer(container, (void **)geom)); 70 } else { 71 PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom)); 72 PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container)); 73 PetscCall(PetscContainerSetPointer(container, (void *)*geom)); 74 PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom)); 75 PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container)); 76 PetscCall(PetscContainerDestroy(&container)); 77 } 78 PetscFunctionReturn(PETSC_SUCCESS); 79 } 80 81 static PetscErrorCode DMPlexRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom) 82 { 83 PetscFunctionBegin; 84 *geom = NULL; 85 PetscFunctionReturn(PETSC_SUCCESS); 86 } 87 88 /*@ 89 DMPlexGetScale - Get the scale for the specified fundamental unit 90 91 Not Collective 92 93 Input Parameters: 94 + dm - the `DM` 95 - unit - The SI unit 96 97 Output Parameter: 98 . scale - The value used to scale all quantities with this unit 99 100 Level: advanced 101 102 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetScale()`, `PetscUnit` 103 @*/ 104 PetscErrorCode DMPlexGetScale(DM dm, PetscUnit unit, PetscReal *scale) 105 { 106 DM_Plex *mesh = (DM_Plex *)dm->data; 107 108 PetscFunctionBegin; 109 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 110 PetscAssertPointer(scale, 3); 111 *scale = mesh->scale[unit]; 112 PetscFunctionReturn(PETSC_SUCCESS); 113 } 114 115 /*@ 116 DMPlexSetScale - Set the scale for the specified fundamental unit 117 118 Not Collective 119 120 Input Parameters: 121 + dm - the `DM` 122 . unit - The SI unit 123 - scale - The value used to scale all quantities with this unit 124 125 Level: advanced 126 127 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetScale()`, `PetscUnit` 128 @*/ 129 PetscErrorCode DMPlexSetScale(DM dm, PetscUnit unit, PetscReal scale) 130 { 131 DM_Plex *mesh = (DM_Plex *)dm->data; 132 133 PetscFunctionBegin; 134 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 135 mesh->scale[unit] = scale; 136 PetscFunctionReturn(PETSC_SUCCESS); 137 } 138 139 PetscErrorCode DMPlexGetUseCeed_Plex(DM dm, PetscBool *useCeed) 140 { 141 DM_Plex *mesh = (DM_Plex *)dm->data; 142 143 PetscFunctionBegin; 144 *useCeed = mesh->useCeed; 145 PetscFunctionReturn(PETSC_SUCCESS); 146 } 147 PetscErrorCode DMPlexSetUseCeed_Plex(DM dm, PetscBool useCeed) 148 { 149 DM_Plex *mesh = (DM_Plex *)dm->data; 150 151 PetscFunctionBegin; 152 mesh->useCeed = useCeed; 153 PetscFunctionReturn(PETSC_SUCCESS); 154 } 155 156 /*@ 157 DMPlexGetUseCeed - Get flag for using the LibCEED backend 158 159 Not collective 160 161 Input Parameter: 162 . dm - The `DM` 163 164 Output Parameter: 165 . useCeed - The flag 166 167 Level: intermediate 168 169 .seealso: `DMPlexSetUseCeed()` 170 @*/ 171 PetscErrorCode DMPlexGetUseCeed(DM dm, PetscBool *useCeed) 172 { 173 PetscFunctionBegin; 174 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 175 PetscAssertPointer(useCeed, 2); 176 *useCeed = PETSC_FALSE; 177 PetscTryMethod(dm, "DMPlexGetUseCeed_C", (DM, PetscBool *), (dm, useCeed)); 178 PetscFunctionReturn(PETSC_SUCCESS); 179 } 180 181 /*@ 182 DMPlexSetUseCeed - Set flag for using the LibCEED backend 183 184 Not collective 185 186 Input Parameters: 187 + dm - The `DM` 188 - useCeed - The flag 189 190 Level: intermediate 191 192 .seealso: `DMPlexGetUseCeed()` 193 @*/ 194 PetscErrorCode DMPlexSetUseCeed(DM dm, PetscBool useCeed) 195 { 196 PetscFunctionBegin; 197 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 198 PetscValidLogicalCollectiveBool(dm, useCeed, 2); 199 PetscUseMethod(dm, "DMPlexSetUseCeed_C", (DM, PetscBool), (dm, useCeed)); 200 PetscFunctionReturn(PETSC_SUCCESS); 201 } 202 203 /*@ 204 DMPlexGetUseMatClosurePermutation - Get flag for using a closure permutation for matrix insertion 205 206 Not collective 207 208 Input Parameter: 209 . dm - The `DM` 210 211 Output Parameter: 212 . useClPerm - The flag 213 214 Level: intermediate 215 216 .seealso: `DMPlexSetUseMatClosurePermutation()` 217 @*/ 218 PetscErrorCode DMPlexGetUseMatClosurePermutation(DM dm, PetscBool *useClPerm) 219 { 220 DM_Plex *mesh = (DM_Plex *)dm->data; 221 222 PetscFunctionBegin; 223 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 224 PetscAssertPointer(useClPerm, 2); 225 *useClPerm = mesh->useMatClPerm; 226 PetscFunctionReturn(PETSC_SUCCESS); 227 } 228 229 /*@ 230 DMPlexSetUseMatClosurePermutation - Set flag for using a closure permutation for matrix insertion 231 232 Not collective 233 234 Input Parameters: 235 + dm - The `DM` 236 - useClPerm - The flag 237 238 Level: intermediate 239 240 .seealso: `DMPlexGetUseMatClosurePermutation()` 241 @*/ 242 PetscErrorCode DMPlexSetUseMatClosurePermutation(DM dm, PetscBool useClPerm) 243 { 244 DM_Plex *mesh = (DM_Plex *)dm->data; 245 246 PetscFunctionBegin; 247 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 248 PetscValidLogicalCollectiveBool(dm, useClPerm, 2); 249 mesh->useMatClPerm = useClPerm; 250 PetscFunctionReturn(PETSC_SUCCESS); 251 } 252 253 static PetscErrorCode DMPlexProjectRigidBody_Private(PetscInt dim, PetscReal t, const PetscReal X[], PetscInt Nc, PetscScalar *mode, void *ctx) 254 { 255 const PetscInt eps[3][3][3] = { 256 {{0, 0, 0}, {0, 0, 1}, {0, -1, 0}}, 257 {{0, 0, -1}, {0, 0, 0}, {1, 0, 0} }, 258 {{0, 1, 0}, {-1, 0, 0}, {0, 0, 0} } 259 }; 260 PetscInt *ctxInt = (PetscInt *)ctx; 261 PetscInt dim2 = ctxInt[0]; 262 PetscInt d = ctxInt[1]; 263 PetscInt i, j, k = dim > 2 ? d - dim : d; 264 265 PetscFunctionBegin; 266 PetscCheck(dim == dim2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Input dimension %" PetscInt_FMT " does not match context dimension %" PetscInt_FMT, dim, dim2); 267 for (i = 0; i < dim; i++) mode[i] = 0.; 268 if (d < dim) { 269 mode[d] = 1.; /* Translation along axis d */ 270 } else { 271 for (i = 0; i < dim; i++) { 272 for (j = 0; j < dim; j++) { mode[j] += eps[i][j][k] * X[i]; /* Rotation about axis d */ } 273 } 274 } 275 PetscFunctionReturn(PETSC_SUCCESS); 276 } 277 278 /*@ 279 DMPlexCreateRigidBody - For the default global section, create rigid body modes by function space interpolation 280 281 Collective 282 283 Input Parameters: 284 + dm - the `DM` 285 - field - The field number for the rigid body space, or 0 for the default 286 287 Output Parameter: 288 . sp - the null space 289 290 Level: advanced 291 292 Note: 293 This is necessary to provide a suitable coarse space for algebraic multigrid 294 295 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()`, `PCGAMG` 296 @*/ 297 PetscErrorCode DMPlexCreateRigidBody(DM dm, PetscInt field, MatNullSpace *sp) 298 { 299 PetscErrorCode (**func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *); 300 MPI_Comm comm; 301 Vec mode[6]; 302 PetscSection section, globalSection; 303 PetscInt dim, dimEmbed, Nf, n, m, mmin, d, i, j; 304 void **ctxs; 305 306 PetscFunctionBegin; 307 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 308 PetscCall(DMGetDimension(dm, &dim)); 309 PetscCall(DMGetCoordinateDim(dm, &dimEmbed)); 310 PetscCall(DMGetNumFields(dm, &Nf)); 311 PetscCheck(!Nf || !(field < 0 || field >= Nf), comm, PETSC_ERR_ARG_OUTOFRANGE, "Field %" PetscInt_FMT " is not in [0, %" PetscInt_FMT ")", field, Nf); 312 if (dim == 1 && Nf < 2) { 313 PetscCall(MatNullSpaceCreate(comm, PETSC_TRUE, 0, NULL, sp)); 314 PetscFunctionReturn(PETSC_SUCCESS); 315 } 316 PetscCall(DMGetLocalSection(dm, §ion)); 317 PetscCall(DMGetGlobalSection(dm, &globalSection)); 318 PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n)); 319 PetscCall(PetscCalloc2(Nf, &func, Nf, &ctxs)); 320 m = (dim * (dim + 1)) / 2; 321 PetscCall(VecCreate(comm, &mode[0])); 322 PetscCall(VecSetType(mode[0], dm->vectype)); 323 PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE)); 324 PetscCall(VecSetUp(mode[0])); 325 PetscCall(VecGetSize(mode[0], &n)); 326 mmin = PetscMin(m, n); 327 func[field] = DMPlexProjectRigidBody_Private; 328 for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i])); 329 for (d = 0; d < m; d++) { 330 PetscInt ctx[2]; 331 332 ctxs[field] = (void *)(&ctx[0]); 333 ctx[0] = dimEmbed; 334 ctx[1] = d; 335 PetscCall(DMProjectFunction(dm, 0.0, func, ctxs, INSERT_VALUES, mode[d])); 336 } 337 /* Orthonormalize system */ 338 for (i = 0; i < mmin; ++i) { 339 PetscScalar dots[6]; 340 341 PetscCall(VecNormalize(mode[i], NULL)); 342 PetscCall(VecMDot(mode[i], mmin - i - 1, mode + i + 1, dots + i + 1)); 343 for (j = i + 1; j < mmin; ++j) { 344 dots[j] *= -1.0; 345 PetscCall(VecAXPY(mode[j], dots[j], mode[i])); 346 } 347 } 348 PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, mmin, mode, sp)); 349 for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i])); 350 PetscCall(PetscFree2(func, ctxs)); 351 PetscFunctionReturn(PETSC_SUCCESS); 352 } 353 354 /*@ 355 DMPlexCreateRigidBodies - For the default global section, create rigid body modes by function space interpolation 356 357 Collective 358 359 Input Parameters: 360 + dm - the `DM` 361 . nb - The number of bodies 362 . label - The `DMLabel` marking each domain 363 . nids - The number of ids per body 364 - ids - An array of the label ids in sequence for each domain 365 366 Output Parameter: 367 . sp - the null space 368 369 Level: advanced 370 371 Note: 372 This is necessary to provide a suitable coarse space for algebraic multigrid 373 374 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `MatNullSpaceCreate()` 375 @*/ 376 PetscErrorCode DMPlexCreateRigidBodies(DM dm, PetscInt nb, DMLabel label, const PetscInt nids[], const PetscInt ids[], MatNullSpace *sp) 377 { 378 MPI_Comm comm; 379 PetscSection section, globalSection; 380 Vec *mode; 381 PetscScalar *dots; 382 PetscInt dim, dimEmbed, n, m, b, d, i, j, off; 383 384 PetscFunctionBegin; 385 PetscCall(PetscObjectGetComm((PetscObject)dm, &comm)); 386 PetscCall(DMGetDimension(dm, &dim)); 387 PetscCall(DMGetCoordinateDim(dm, &dimEmbed)); 388 PetscCall(DMGetLocalSection(dm, §ion)); 389 PetscCall(DMGetGlobalSection(dm, &globalSection)); 390 PetscCall(PetscSectionGetConstrainedStorageSize(globalSection, &n)); 391 m = nb * (dim * (dim + 1)) / 2; 392 PetscCall(PetscMalloc2(m, &mode, m, &dots)); 393 PetscCall(VecCreate(comm, &mode[0])); 394 PetscCall(VecSetSizes(mode[0], n, PETSC_DETERMINE)); 395 PetscCall(VecSetUp(mode[0])); 396 for (i = 1; i < m; ++i) PetscCall(VecDuplicate(mode[0], &mode[i])); 397 for (b = 0, off = 0; b < nb; ++b) { 398 for (d = 0; d < m / nb; ++d) { 399 PetscInt ctx[2]; 400 PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal *, PetscInt, PetscScalar *, void *) = DMPlexProjectRigidBody_Private; 401 void *voidctx = (void *)(&ctx[0]); 402 403 ctx[0] = dimEmbed; 404 ctx[1] = d; 405 PetscCall(DMProjectFunctionLabel(dm, 0.0, label, nids[b], &ids[off], 0, NULL, &func, &voidctx, INSERT_VALUES, mode[d])); 406 off += nids[b]; 407 } 408 } 409 /* Orthonormalize system */ 410 for (i = 0; i < m; ++i) { 411 PetscScalar dots[6]; 412 413 PetscCall(VecNormalize(mode[i], NULL)); 414 PetscCall(VecMDot(mode[i], m - i - 1, mode + i + 1, dots + i + 1)); 415 for (j = i + 1; j < m; ++j) { 416 dots[j] *= -1.0; 417 PetscCall(VecAXPY(mode[j], dots[j], mode[i])); 418 } 419 } 420 PetscCall(MatNullSpaceCreate(comm, PETSC_FALSE, m, mode, sp)); 421 for (i = 0; i < m; ++i) PetscCall(VecDestroy(&mode[i])); 422 PetscCall(PetscFree2(mode, dots)); 423 PetscFunctionReturn(PETSC_SUCCESS); 424 } 425 426 /*@ 427 DMPlexSetMaxProjectionHeight - In DMPlexProjectXXXLocal() functions, the projected values of a basis function's dofs 428 are computed by associating the basis function with one of the mesh points in its transitively-closed support, and 429 evaluating the dual space basis of that point. 430 431 Input Parameters: 432 + dm - the `DMPLEX` object 433 - height - the maximum projection height >= 0 434 435 Level: advanced 436 437 Notes: 438 A basis function is associated with the point in its transitively-closed support whose mesh 439 height is highest (w.r.t. DAG height), but not greater than the maximum projection height, 440 which is set with this function. By default, the maximum projection height is zero, which 441 means that only mesh cells are used to project basis functions. A height of one, for 442 example, evaluates a cell-interior basis functions using its cells dual space basis, but all 443 other basis functions with the dual space basis of a face. 444 445 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()` 446 @*/ 447 PetscErrorCode DMPlexSetMaxProjectionHeight(DM dm, PetscInt height) 448 { 449 DM_Plex *plex = (DM_Plex *)dm->data; 450 451 PetscFunctionBegin; 452 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 453 plex->maxProjectionHeight = height; 454 PetscFunctionReturn(PETSC_SUCCESS); 455 } 456 457 /*@ 458 DMPlexGetMaxProjectionHeight - Get the maximum height (w.r.t. DAG) of mesh points used to evaluate dual bases in 459 DMPlexProjectXXXLocal() functions. 460 461 Input Parameter: 462 . dm - the `DMPLEX` object 463 464 Output Parameter: 465 . height - the maximum projection height 466 467 Level: intermediate 468 469 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetMaxProjectionHeight()`, `DMProjectFunctionLocal()`, `DMProjectFunctionLabelLocal()` 470 @*/ 471 PetscErrorCode DMPlexGetMaxProjectionHeight(DM dm, PetscInt *height) 472 { 473 DM_Plex *plex = (DM_Plex *)dm->data; 474 475 PetscFunctionBegin; 476 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 477 *height = plex->maxProjectionHeight; 478 PetscFunctionReturn(PETSC_SUCCESS); 479 } 480 481 typedef struct { 482 PetscReal alpha; /* The first Euler angle, and in 2D the only one */ 483 PetscReal beta; /* The second Euler angle */ 484 PetscReal gamma; /* The third Euler angle */ 485 PetscInt dim; /* The dimension of R */ 486 PetscScalar *R; /* The rotation matrix, transforming a vector in the local basis to the global basis */ 487 PetscScalar *RT; /* The transposed rotation matrix, transforming a vector in the global basis to the local basis */ 488 } RotCtx; 489 490 /* 491 Note: Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that 492 we rotate with respect to a fixed initial coordinate system, the local basis (x-y-z). The global basis (X-Y-Z) is reached as follows: 493 $ The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis. 494 $ The XYZ system rotates again about the x axis by beta. The Z axis is now at angle beta with respect to the z axis. 495 $ The XYZ system rotates a third time about the z axis by gamma. 496 */ 497 static PetscErrorCode DMPlexBasisTransformSetUp_Rotation_Internal(DM dm, void *ctx) 498 { 499 RotCtx *rc = (RotCtx *)ctx; 500 PetscInt dim = rc->dim; 501 PetscReal c1, s1, c2, s2, c3, s3; 502 503 PetscFunctionBegin; 504 PetscCall(PetscMalloc2(PetscSqr(dim), &rc->R, PetscSqr(dim), &rc->RT)); 505 switch (dim) { 506 case 2: 507 c1 = PetscCosReal(rc->alpha); 508 s1 = PetscSinReal(rc->alpha); 509 rc->R[0] = c1; 510 rc->R[1] = s1; 511 rc->R[2] = -s1; 512 rc->R[3] = c1; 513 PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim))); 514 DMPlex_Transpose2D_Internal(rc->RT); 515 break; 516 case 3: 517 c1 = PetscCosReal(rc->alpha); 518 s1 = PetscSinReal(rc->alpha); 519 c2 = PetscCosReal(rc->beta); 520 s2 = PetscSinReal(rc->beta); 521 c3 = PetscCosReal(rc->gamma); 522 s3 = PetscSinReal(rc->gamma); 523 rc->R[0] = c1 * c3 - c2 * s1 * s3; 524 rc->R[1] = c3 * s1 + c1 * c2 * s3; 525 rc->R[2] = s2 * s3; 526 rc->R[3] = -c1 * s3 - c2 * c3 * s1; 527 rc->R[4] = c1 * c2 * c3 - s1 * s3; 528 rc->R[5] = c3 * s2; 529 rc->R[6] = s1 * s2; 530 rc->R[7] = -c1 * s2; 531 rc->R[8] = c2; 532 PetscCall(PetscArraycpy(rc->RT, rc->R, PetscSqr(dim))); 533 DMPlex_Transpose3D_Internal(rc->RT); 534 break; 535 default: 536 SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Dimension %" PetscInt_FMT " not supported", dim); 537 } 538 PetscFunctionReturn(PETSC_SUCCESS); 539 } 540 541 static PetscErrorCode DMPlexBasisTransformDestroy_Rotation_Internal(DM dm, void *ctx) 542 { 543 RotCtx *rc = (RotCtx *)ctx; 544 545 PetscFunctionBegin; 546 PetscCall(PetscFree2(rc->R, rc->RT)); 547 PetscCall(PetscFree(rc)); 548 PetscFunctionReturn(PETSC_SUCCESS); 549 } 550 551 static PetscErrorCode DMPlexBasisTransformGetMatrix_Rotation_Internal(DM dm, const PetscReal x[], PetscBool l2g, const PetscScalar **A, void *ctx) 552 { 553 RotCtx *rc = (RotCtx *)ctx; 554 555 PetscFunctionBeginHot; 556 PetscAssertPointer(ctx, 5); 557 if (l2g) { 558 *A = rc->R; 559 } else { 560 *A = rc->RT; 561 } 562 PetscFunctionReturn(PETSC_SUCCESS); 563 } 564 565 PetscErrorCode DMPlexBasisTransformApplyReal_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscReal *y, PetscReal *z, void *ctx) 566 { 567 PetscFunctionBegin; 568 #if defined(PETSC_USE_COMPLEX) 569 switch (dim) { 570 case 2: { 571 PetscScalar yt[2] = {y[0], y[1]}, zt[2] = {0.0, 0.0}; 572 573 PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx)); 574 z[0] = PetscRealPart(zt[0]); 575 z[1] = PetscRealPart(zt[1]); 576 } break; 577 case 3: { 578 PetscScalar yt[3] = {y[0], y[1], y[2]}, zt[3] = {0.0, 0.0, 0.0}; 579 580 PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, yt, zt, ctx)); 581 z[0] = PetscRealPart(zt[0]); 582 z[1] = PetscRealPart(zt[1]); 583 z[2] = PetscRealPart(zt[2]); 584 } break; 585 } 586 #else 587 PetscCall(DMPlexBasisTransformApply_Internal(dm, x, l2g, dim, y, z, ctx)); 588 #endif 589 PetscFunctionReturn(PETSC_SUCCESS); 590 } 591 592 PetscErrorCode DMPlexBasisTransformApply_Internal(DM dm, const PetscReal x[], PetscBool l2g, PetscInt dim, const PetscScalar *y, PetscScalar *z, void *ctx) 593 { 594 const PetscScalar *A; 595 596 PetscFunctionBeginHot; 597 PetscCall((*dm->transformGetMatrix)(dm, x, l2g, &A, ctx)); 598 switch (dim) { 599 case 2: 600 DMPlex_Mult2D_Internal(A, 1, y, z); 601 break; 602 case 3: 603 DMPlex_Mult3D_Internal(A, 1, y, z); 604 break; 605 } 606 PetscFunctionReturn(PETSC_SUCCESS); 607 } 608 609 static PetscErrorCode DMPlexBasisTransformField_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscInt f, PetscBool l2g, PetscScalar *a) 610 { 611 PetscSection ts; 612 const PetscScalar *ta, *tva; 613 PetscInt dof; 614 615 PetscFunctionBeginHot; 616 PetscCall(DMGetLocalSection(tdm, &ts)); 617 PetscCall(PetscSectionGetFieldDof(ts, p, f, &dof)); 618 PetscCall(VecGetArrayRead(tv, &ta)); 619 PetscCall(DMPlexPointLocalFieldRead(tdm, p, f, ta, &tva)); 620 if (l2g) { 621 switch (dof) { 622 case 4: 623 DMPlex_Mult2D_Internal(tva, 1, a, a); 624 break; 625 case 9: 626 DMPlex_Mult3D_Internal(tva, 1, a, a); 627 break; 628 } 629 } else { 630 switch (dof) { 631 case 4: 632 DMPlex_MultTranspose2D_Internal(tva, 1, a, a); 633 break; 634 case 9: 635 DMPlex_MultTranspose3D_Internal(tva, 1, a, a); 636 break; 637 } 638 } 639 PetscCall(VecRestoreArrayRead(tv, &ta)); 640 PetscFunctionReturn(PETSC_SUCCESS); 641 } 642 643 static PetscErrorCode DMPlexBasisTransformFieldTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt pf, PetscInt f, PetscInt pg, PetscInt g, PetscBool l2g, PetscInt lda, PetscScalar *a) 644 { 645 PetscSection s, ts; 646 const PetscScalar *ta, *tvaf, *tvag; 647 PetscInt fdof, gdof, fpdof, gpdof; 648 649 PetscFunctionBeginHot; 650 PetscCall(DMGetLocalSection(dm, &s)); 651 PetscCall(DMGetLocalSection(tdm, &ts)); 652 PetscCall(PetscSectionGetFieldDof(s, pf, f, &fpdof)); 653 PetscCall(PetscSectionGetFieldDof(s, pg, g, &gpdof)); 654 PetscCall(PetscSectionGetFieldDof(ts, pf, f, &fdof)); 655 PetscCall(PetscSectionGetFieldDof(ts, pg, g, &gdof)); 656 PetscCall(VecGetArrayRead(tv, &ta)); 657 PetscCall(DMPlexPointLocalFieldRead(tdm, pf, f, ta, &tvaf)); 658 PetscCall(DMPlexPointLocalFieldRead(tdm, pg, g, ta, &tvag)); 659 if (l2g) { 660 switch (fdof) { 661 case 4: 662 DMPlex_MatMult2D_Internal(tvaf, gpdof, lda, a, a); 663 break; 664 case 9: 665 DMPlex_MatMult3D_Internal(tvaf, gpdof, lda, a, a); 666 break; 667 } 668 switch (gdof) { 669 case 4: 670 DMPlex_MatMultTransposeLeft2D_Internal(tvag, fpdof, lda, a, a); 671 break; 672 case 9: 673 DMPlex_MatMultTransposeLeft3D_Internal(tvag, fpdof, lda, a, a); 674 break; 675 } 676 } else { 677 switch (fdof) { 678 case 4: 679 DMPlex_MatMultTranspose2D_Internal(tvaf, gpdof, lda, a, a); 680 break; 681 case 9: 682 DMPlex_MatMultTranspose3D_Internal(tvaf, gpdof, lda, a, a); 683 break; 684 } 685 switch (gdof) { 686 case 4: 687 DMPlex_MatMultLeft2D_Internal(tvag, fpdof, lda, a, a); 688 break; 689 case 9: 690 DMPlex_MatMultLeft3D_Internal(tvag, fpdof, lda, a, a); 691 break; 692 } 693 } 694 PetscCall(VecRestoreArrayRead(tv, &ta)); 695 PetscFunctionReturn(PETSC_SUCCESS); 696 } 697 698 PetscErrorCode DMPlexBasisTransformPoint_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool fieldActive[], PetscBool l2g, PetscScalar *a) 699 { 700 PetscSection s; 701 PetscSection clSection; 702 IS clPoints; 703 const PetscInt *clp; 704 PetscInt *points = NULL; 705 PetscInt Nf, f, Np, cp, dof, d = 0; 706 707 PetscFunctionBegin; 708 PetscCall(DMGetLocalSection(dm, &s)); 709 PetscCall(PetscSectionGetNumFields(s, &Nf)); 710 PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp)); 711 for (f = 0; f < Nf; ++f) { 712 for (cp = 0; cp < Np * 2; cp += 2) { 713 PetscCall(PetscSectionGetFieldDof(s, points[cp], f, &dof)); 714 if (!dof) continue; 715 if (fieldActive[f]) PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, points[cp], f, l2g, &a[d])); 716 d += dof; 717 } 718 } 719 PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp)); 720 PetscFunctionReturn(PETSC_SUCCESS); 721 } 722 723 PetscErrorCode DMPlexBasisTransformPointTensor_Internal(DM dm, DM tdm, Vec tv, PetscInt p, PetscBool l2g, PetscInt lda, PetscScalar *a) 724 { 725 PetscSection s; 726 PetscSection clSection; 727 IS clPoints; 728 const PetscInt *clp; 729 PetscInt *points = NULL; 730 PetscInt Nf, f, g, Np, cpf, cpg, fdof, gdof, r, c = 0; 731 732 PetscFunctionBegin; 733 PetscCall(DMGetLocalSection(dm, &s)); 734 PetscCall(PetscSectionGetNumFields(s, &Nf)); 735 PetscCall(DMPlexGetCompressedClosure(dm, s, p, 0, &Np, &points, &clSection, &clPoints, &clp)); 736 for (f = 0, r = 0; f < Nf; ++f) { 737 for (cpf = 0; cpf < Np * 2; cpf += 2) { 738 PetscCall(PetscSectionGetFieldDof(s, points[cpf], f, &fdof)); 739 for (g = 0, c = 0; g < Nf; ++g) { 740 for (cpg = 0; cpg < Np * 2; cpg += 2) { 741 PetscCall(PetscSectionGetFieldDof(s, points[cpg], g, &gdof)); 742 PetscCall(DMPlexBasisTransformFieldTensor_Internal(dm, tdm, tv, points[cpf], f, points[cpg], g, l2g, lda, &a[r * lda + c])); 743 c += gdof; 744 } 745 } 746 PetscCheck(c == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of columns %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda); 747 r += fdof; 748 } 749 } 750 PetscCheck(r == lda, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid number of rows %" PetscInt_FMT " should be %" PetscInt_FMT, c, lda); 751 PetscCall(DMPlexRestoreCompressedClosure(dm, s, p, &Np, &points, &clSection, &clPoints, &clp)); 752 PetscFunctionReturn(PETSC_SUCCESS); 753 } 754 755 static PetscErrorCode DMPlexBasisTransform_Internal(DM dm, Vec lv, PetscBool l2g) 756 { 757 DM tdm; 758 Vec tv; 759 PetscSection ts, s; 760 const PetscScalar *ta; 761 PetscScalar *a, *va; 762 PetscInt pStart, pEnd, p, Nf, f; 763 764 PetscFunctionBegin; 765 PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm)); 766 PetscCall(DMGetBasisTransformVec_Internal(dm, &tv)); 767 PetscCall(DMGetLocalSection(tdm, &ts)); 768 PetscCall(DMGetLocalSection(dm, &s)); 769 PetscCall(PetscSectionGetChart(s, &pStart, &pEnd)); 770 PetscCall(PetscSectionGetNumFields(s, &Nf)); 771 PetscCall(VecGetArray(lv, &a)); 772 PetscCall(VecGetArrayRead(tv, &ta)); 773 for (p = pStart; p < pEnd; ++p) { 774 for (f = 0; f < Nf; ++f) { 775 PetscCall(DMPlexPointLocalFieldRef(dm, p, f, a, &va)); 776 PetscCall(DMPlexBasisTransformField_Internal(dm, tdm, tv, p, f, l2g, va)); 777 } 778 } 779 PetscCall(VecRestoreArray(lv, &a)); 780 PetscCall(VecRestoreArrayRead(tv, &ta)); 781 PetscFunctionReturn(PETSC_SUCCESS); 782 } 783 784 /*@ 785 DMPlexGlobalToLocalBasis - Transform the values in the given local vector from the global basis to the local basis 786 787 Input Parameters: 788 + dm - The `DM` 789 - lv - A local vector with values in the global basis 790 791 Output Parameter: 792 . lv - A local vector with values in the local basis 793 794 Level: developer 795 796 Note: 797 This method is only intended to be called inside `DMGlobalToLocal()`. It is unlikely that a user will have a local vector full of coefficients for the global basis unless they are reimplementing GlobalToLocal. 798 799 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexLocalToGlobalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()` 800 @*/ 801 PetscErrorCode DMPlexGlobalToLocalBasis(DM dm, Vec lv) 802 { 803 PetscFunctionBegin; 804 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 805 PetscValidHeaderSpecific(lv, VEC_CLASSID, 2); 806 PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_FALSE)); 807 PetscFunctionReturn(PETSC_SUCCESS); 808 } 809 810 /*@ 811 DMPlexLocalToGlobalBasis - Transform the values in the given local vector from the local basis to the global basis 812 813 Input Parameters: 814 + dm - The `DM` 815 - lv - A local vector with values in the local basis 816 817 Output Parameter: 818 . lv - A local vector with values in the global basis 819 820 Level: developer 821 822 Note: 823 This method is only intended to be called inside `DMGlobalToLocal()`. It is unlikely that a user would want a local vector full of coefficients for the global basis unless they are reimplementing GlobalToLocal. 824 825 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMGetLocalSection()`, `DMPlexCreateBasisRotation()` 826 @*/ 827 PetscErrorCode DMPlexLocalToGlobalBasis(DM dm, Vec lv) 828 { 829 PetscFunctionBegin; 830 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 831 PetscValidHeaderSpecific(lv, VEC_CLASSID, 2); 832 PetscCall(DMPlexBasisTransform_Internal(dm, lv, PETSC_TRUE)); 833 PetscFunctionReturn(PETSC_SUCCESS); 834 } 835 836 /*@ 837 DMPlexCreateBasisRotation - Create an internal transformation from the global basis, used to specify boundary conditions 838 and global solutions, to a local basis, appropriate for discretization integrals and assembly. 839 840 Input Parameters: 841 + dm - The `DM` 842 . alpha - The first Euler angle, and in 2D the only one 843 . beta - The second Euler angle 844 - gamma - The third Euler angle 845 846 Level: developer 847 848 Note: 849 Following https://en.wikipedia.org/wiki/Euler_angles, we will specify Euler angles by extrinsic rotations, meaning that 850 we rotate with respect to a fixed initial coordinate system, the local basis (x-y-z). The global basis (X-Y-Z) is reached as follows 851 .vb 852 The XYZ system rotates about the z axis by alpha. The X axis is now at angle alpha with respect to the x axis. 853 The XYZ system rotates again about the x axis by beta. The Z axis is now at angle beta with respect to the z axis. 854 The XYZ system rotates a third time about the z axis by gamma. 855 .ve 856 857 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGlobalToLocalBasis()`, `DMPlexLocalToGlobalBasis()` 858 @*/ 859 PetscErrorCode DMPlexCreateBasisRotation(DM dm, PetscReal alpha, PetscReal beta, PetscReal gamma) 860 { 861 RotCtx *rc; 862 PetscInt cdim; 863 864 PetscFunctionBegin; 865 PetscCall(DMGetCoordinateDim(dm, &cdim)); 866 PetscCall(PetscMalloc1(1, &rc)); 867 dm->transformCtx = rc; 868 dm->transformSetUp = DMPlexBasisTransformSetUp_Rotation_Internal; 869 dm->transformDestroy = DMPlexBasisTransformDestroy_Rotation_Internal; 870 dm->transformGetMatrix = DMPlexBasisTransformGetMatrix_Rotation_Internal; 871 rc->dim = cdim; 872 rc->alpha = alpha; 873 rc->beta = beta; 874 rc->gamma = gamma; 875 PetscCall((*dm->transformSetUp)(dm, dm->transformCtx)); 876 PetscCall(DMConstructBasisTransform_Internal(dm)); 877 PetscFunctionReturn(PETSC_SUCCESS); 878 } 879 880 /*@C 881 DMPlexInsertBoundaryValuesEssential - Insert boundary values into a local vector using a function of the coordinates 882 883 Input Parameters: 884 + dm - The `DM`, with a `PetscDS` that matches the problem being constrained 885 . time - The time 886 . field - The field to constrain 887 . Nc - The number of constrained field components, or 0 for all components 888 . comps - An array of constrained component numbers, or `NULL` for all components 889 . label - The `DMLabel` defining constrained points 890 . numids - The number of `DMLabel` ids for constrained points 891 . ids - An array of ids for constrained points 892 . func - A pointwise function giving boundary values 893 - ctx - An optional user context for bcFunc 894 895 Output Parameter: 896 . locX - A local vector to receives the boundary values 897 898 Level: developer 899 900 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLabel`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()` 901 @*/ 902 PetscErrorCode DMPlexInsertBoundaryValuesEssential(DM dm, PetscReal time, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void *ctx, Vec locX) 903 { 904 PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal x[], PetscInt, PetscScalar *u, void *ctx); 905 void **ctxs; 906 PetscInt numFields; 907 908 PetscFunctionBegin; 909 PetscCall(DMGetNumFields(dm, &numFields)); 910 PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs)); 911 funcs[field] = func; 912 ctxs[field] = ctx; 913 PetscCall(DMProjectFunctionLabelLocal(dm, time, label, numids, ids, Nc, comps, funcs, ctxs, INSERT_BC_VALUES, locX)); 914 PetscCall(PetscFree2(funcs, ctxs)); 915 PetscFunctionReturn(PETSC_SUCCESS); 916 } 917 918 /*@C 919 DMPlexInsertBoundaryValuesEssentialField - Insert boundary values into a local vector using a function of the coordinates and field data 920 921 Input Parameters: 922 + dm - The `DM`, with a `PetscDS` that matches the problem being constrained 923 . time - The time 924 . locU - A local vector with the input solution values 925 . field - The field to constrain 926 . Nc - The number of constrained field components, or 0 for all components 927 . comps - An array of constrained component numbers, or `NULL` for all components 928 . label - The `DMLabel` defining constrained points 929 . numids - The number of `DMLabel` ids for constrained points 930 . ids - An array of ids for constrained points 931 . func - A pointwise function giving boundary values 932 - ctx - An optional user context for bcFunc 933 934 Output Parameter: 935 . locX - A local vector to receives the boundary values 936 937 Level: developer 938 939 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialBdField()`, `DMAddBoundary()` 940 @*/ 941 PetscErrorCode DMPlexInsertBoundaryValuesEssentialField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), void *ctx, Vec locX) 942 { 943 void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]); 944 void **ctxs; 945 PetscInt numFields; 946 947 PetscFunctionBegin; 948 PetscCall(DMGetNumFields(dm, &numFields)); 949 PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs)); 950 funcs[field] = func; 951 ctxs[field] = ctx; 952 PetscCall(DMProjectFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX)); 953 PetscCall(PetscFree2(funcs, ctxs)); 954 PetscFunctionReturn(PETSC_SUCCESS); 955 } 956 957 /*@C 958 DMPlexInsertBoundaryValuesEssentialBdField - Insert boundary values into a local vector using a function of the coordinates and boundary field data 959 960 Collective 961 962 Input Parameters: 963 + dm - The `DM`, with a `PetscDS` that matches the problem being constrained 964 . time - The time 965 . locU - A local vector with the input solution values 966 . field - The field to constrain 967 . Nc - The number of constrained field components, or 0 for all components 968 . comps - An array of constrained component numbers, or `NULL` for all components 969 . label - The `DMLabel` defining constrained points 970 . numids - The number of `DMLabel` ids for constrained points 971 . ids - An array of ids for constrained points 972 . func - A pointwise function giving boundary values, the calling sequence is given in `DMProjectBdFieldLabelLocal()` 973 - ctx - An optional user context for `func` 974 975 Output Parameter: 976 . locX - A local vector to receive the boundary values 977 978 Level: developer 979 980 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectBdFieldLabelLocal()`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()` 981 @*/ 982 PetscErrorCode DMPlexInsertBoundaryValuesEssentialBdField(DM dm, PetscReal time, Vec locU, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), void *ctx, Vec locX) 983 { 984 void (**funcs)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]); 985 void **ctxs; 986 PetscInt numFields; 987 988 PetscFunctionBegin; 989 PetscCall(DMGetNumFields(dm, &numFields)); 990 PetscCall(PetscCalloc2(numFields, &funcs, numFields, &ctxs)); 991 funcs[field] = func; 992 ctxs[field] = ctx; 993 PetscCall(DMProjectBdFieldLabelLocal(dm, time, label, numids, ids, Nc, comps, locU, funcs, INSERT_BC_VALUES, locX)); 994 PetscCall(PetscFree2(funcs, ctxs)); 995 PetscFunctionReturn(PETSC_SUCCESS); 996 } 997 998 /*@C 999 DMPlexInsertBoundaryValuesRiemann - Insert boundary values into a local vector 1000 1001 Input Parameters: 1002 + dm - The `DM`, with a `PetscDS` that matches the problem being constrained 1003 . time - The time 1004 . faceGeometry - A vector with the FVM face geometry information 1005 . cellGeometry - A vector with the FVM cell geometry information 1006 . Grad - A vector with the FVM cell gradient information 1007 . field - The field to constrain 1008 . Nc - The number of constrained field components, or 0 for all components 1009 . comps - An array of constrained component numbers, or `NULL` for all components 1010 . label - The `DMLabel` defining constrained points 1011 . numids - The number of `DMLabel` ids for constrained points 1012 . ids - An array of ids for constrained points 1013 . func - A pointwise function giving boundary values 1014 - ctx - An optional user context for bcFunc 1015 1016 Output Parameter: 1017 . locX - A local vector to receives the boundary values 1018 1019 Level: developer 1020 1021 Note: 1022 This implementation currently ignores the numcomps/comps argument from `DMAddBoundary()` 1023 1024 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexInsertBoundaryValuesEssential()`, `DMPlexInsertBoundaryValuesEssentialField()`, `DMAddBoundary()` 1025 @*/ 1026 PetscErrorCode DMPlexInsertBoundaryValuesRiemann(DM dm, PetscReal time, Vec faceGeometry, Vec cellGeometry, Vec Grad, PetscInt field, PetscInt Nc, const PetscInt comps[], DMLabel label, PetscInt numids, const PetscInt ids[], PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *), void *ctx, Vec locX) 1027 { 1028 PetscDS prob; 1029 PetscSF sf; 1030 DM dmFace, dmCell, dmGrad; 1031 const PetscScalar *facegeom, *cellgeom = NULL, *grad; 1032 const PetscInt *leaves; 1033 PetscScalar *x, *fx; 1034 PetscInt dim, nleaves, loc, fStart, fEnd, pdim, i; 1035 PetscErrorCode ierru = PETSC_SUCCESS; 1036 1037 PetscFunctionBegin; 1038 PetscCall(DMGetPointSF(dm, &sf)); 1039 PetscCall(PetscSFGetGraph(sf, NULL, &nleaves, &leaves, NULL)); 1040 nleaves = PetscMax(0, nleaves); 1041 PetscCall(DMGetDimension(dm, &dim)); 1042 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 1043 PetscCall(DMGetDS(dm, &prob)); 1044 PetscCall(VecGetDM(faceGeometry, &dmFace)); 1045 PetscCall(VecGetArrayRead(faceGeometry, &facegeom)); 1046 if (cellGeometry) { 1047 PetscCall(VecGetDM(cellGeometry, &dmCell)); 1048 PetscCall(VecGetArrayRead(cellGeometry, &cellgeom)); 1049 } 1050 if (Grad) { 1051 PetscFV fv; 1052 1053 PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fv)); 1054 PetscCall(VecGetDM(Grad, &dmGrad)); 1055 PetscCall(VecGetArrayRead(Grad, &grad)); 1056 PetscCall(PetscFVGetNumComponents(fv, &pdim)); 1057 PetscCall(DMGetWorkArray(dm, pdim, MPIU_SCALAR, &fx)); 1058 } 1059 PetscCall(VecGetArray(locX, &x)); 1060 for (i = 0; i < numids; ++i) { 1061 IS faceIS; 1062 const PetscInt *faces; 1063 PetscInt numFaces, f; 1064 1065 PetscCall(DMLabelGetStratumIS(label, ids[i], &faceIS)); 1066 if (!faceIS) continue; /* No points with that id on this process */ 1067 PetscCall(ISGetLocalSize(faceIS, &numFaces)); 1068 PetscCall(ISGetIndices(faceIS, &faces)); 1069 for (f = 0; f < numFaces; ++f) { 1070 const PetscInt face = faces[f], *cells; 1071 PetscFVFaceGeom *fg; 1072 1073 if ((face < fStart) || (face >= fEnd)) continue; /* Refinement adds non-faces to labels */ 1074 PetscCall(PetscFindInt(face, nleaves, (PetscInt *)leaves, &loc)); 1075 if (loc >= 0) continue; 1076 PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg)); 1077 PetscCall(DMPlexGetSupport(dm, face, &cells)); 1078 if (Grad) { 1079 PetscFVCellGeom *cg; 1080 PetscScalar *cx, *cgrad; 1081 PetscScalar *xG; 1082 PetscReal dx[3]; 1083 PetscInt d; 1084 1085 PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cg)); 1086 PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &cx)); 1087 PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], grad, &cgrad)); 1088 PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG)); 1089 DMPlex_WaxpyD_Internal(dim, -1, cg->centroid, fg->centroid, dx); 1090 for (d = 0; d < pdim; ++d) fx[d] = cx[d] + DMPlex_DotD_Internal(dim, &cgrad[d * dim], dx); 1091 PetscCall((*func)(time, fg->centroid, fg->normal, fx, xG, ctx)); 1092 } else { 1093 PetscScalar *xI; 1094 PetscScalar *xG; 1095 1096 PetscCall(DMPlexPointLocalRead(dm, cells[0], x, &xI)); 1097 PetscCall(DMPlexPointLocalFieldRef(dm, cells[1], field, x, &xG)); 1098 ierru = (*func)(time, fg->centroid, fg->normal, xI, xG, ctx); 1099 if (ierru) { 1100 PetscCall(ISRestoreIndices(faceIS, &faces)); 1101 PetscCall(ISDestroy(&faceIS)); 1102 goto cleanup; 1103 } 1104 } 1105 } 1106 PetscCall(ISRestoreIndices(faceIS, &faces)); 1107 PetscCall(ISDestroy(&faceIS)); 1108 } 1109 cleanup: 1110 PetscCall(VecRestoreArray(locX, &x)); 1111 if (Grad) { 1112 PetscCall(DMRestoreWorkArray(dm, pdim, MPIU_SCALAR, &fx)); 1113 PetscCall(VecRestoreArrayRead(Grad, &grad)); 1114 } 1115 if (cellGeometry) PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom)); 1116 PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom)); 1117 PetscCall(ierru); 1118 PetscFunctionReturn(PETSC_SUCCESS); 1119 } 1120 1121 static PetscErrorCode zero(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx) 1122 { 1123 PetscInt c; 1124 for (c = 0; c < Nc; ++c) u[c] = 0.0; 1125 return PETSC_SUCCESS; 1126 } 1127 1128 PetscErrorCode DMPlexInsertBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM) 1129 { 1130 PetscObject isZero; 1131 PetscDS prob; 1132 PetscInt numBd, b; 1133 1134 PetscFunctionBegin; 1135 PetscCall(DMGetDS(dm, &prob)); 1136 PetscCall(PetscDSGetNumBoundary(prob, &numBd)); 1137 PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero)); 1138 for (b = 0; b < numBd; ++b) { 1139 PetscWeakForm wf; 1140 DMBoundaryConditionType type; 1141 const char *name; 1142 DMLabel label; 1143 PetscInt field, Nc; 1144 const PetscInt *comps; 1145 PetscObject obj; 1146 PetscClassId id; 1147 void (*bvfunc)(void); 1148 PetscInt numids; 1149 const PetscInt *ids; 1150 void *ctx; 1151 1152 PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, &bvfunc, NULL, &ctx)); 1153 if (insertEssential != (type & DM_BC_ESSENTIAL)) continue; 1154 PetscCall(DMGetField(dm, field, NULL, &obj)); 1155 PetscCall(PetscObjectGetClassId(obj, &id)); 1156 if (id == PETSCFE_CLASSID) { 1157 switch (type) { 1158 /* for FEM, there is no insertion to be done for non-essential boundary conditions */ 1159 case DM_BC_ESSENTIAL: { 1160 PetscSimplePointFunc func = (PetscSimplePointFunc)bvfunc; 1161 1162 if (isZero) func = zero; 1163 PetscCall(DMPlexLabelAddCells(dm, label)); 1164 PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func, ctx, locX)); 1165 PetscCall(DMPlexLabelClearCells(dm, label)); 1166 } break; 1167 case DM_BC_ESSENTIAL_FIELD: { 1168 PetscPointFunc func = (PetscPointFunc)bvfunc; 1169 1170 PetscCall(DMPlexLabelAddCells(dm, label)); 1171 PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func, ctx, locX)); 1172 PetscCall(DMPlexLabelClearCells(dm, label)); 1173 } break; 1174 default: 1175 break; 1176 } 1177 } else if (id == PETSCFV_CLASSID) { 1178 { 1179 PetscErrorCode (*func)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *) = (PetscErrorCode(*)(PetscReal, const PetscReal *, const PetscReal *, const PetscScalar *, PetscScalar *, void *))bvfunc; 1180 1181 if (!faceGeomFVM) continue; 1182 PetscCall(DMPlexInsertBoundaryValuesRiemann(dm, time, faceGeomFVM, cellGeomFVM, gradFVM, field, Nc, comps, label, numids, ids, func, ctx, locX)); 1183 } 1184 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 1185 } 1186 PetscFunctionReturn(PETSC_SUCCESS); 1187 } 1188 1189 PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues_Plex(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM) 1190 { 1191 PetscObject isZero; 1192 PetscDS prob; 1193 PetscInt numBd, b; 1194 1195 PetscFunctionBegin; 1196 if (!locX) PetscFunctionReturn(PETSC_SUCCESS); 1197 PetscCall(DMGetDS(dm, &prob)); 1198 PetscCall(PetscDSGetNumBoundary(prob, &numBd)); 1199 PetscCall(PetscObjectQuery((PetscObject)locX, "__Vec_bc_zero__", &isZero)); 1200 for (b = 0; b < numBd; ++b) { 1201 PetscWeakForm wf; 1202 DMBoundaryConditionType type; 1203 const char *name; 1204 DMLabel label; 1205 PetscInt field, Nc; 1206 const PetscInt *comps; 1207 PetscObject obj; 1208 PetscClassId id; 1209 PetscInt numids; 1210 const PetscInt *ids; 1211 void (*bvfunc)(void); 1212 void *ctx; 1213 1214 PetscCall(PetscDSGetBoundary(prob, b, &wf, &type, &name, &label, &numids, &ids, &field, &Nc, &comps, NULL, &bvfunc, &ctx)); 1215 if (insertEssential != (type & DM_BC_ESSENTIAL)) continue; 1216 PetscCall(DMGetField(dm, field, NULL, &obj)); 1217 PetscCall(PetscObjectGetClassId(obj, &id)); 1218 if (id == PETSCFE_CLASSID) { 1219 switch (type) { 1220 /* for FEM, there is no insertion to be done for non-essential boundary conditions */ 1221 case DM_BC_ESSENTIAL: { 1222 PetscSimplePointFunc func_t = (PetscSimplePointFunc)bvfunc; 1223 1224 if (isZero) func_t = zero; 1225 PetscCall(DMPlexLabelAddCells(dm, label)); 1226 PetscCall(DMPlexInsertBoundaryValuesEssential(dm, time, field, Nc, comps, label, numids, ids, func_t, ctx, locX)); 1227 PetscCall(DMPlexLabelClearCells(dm, label)); 1228 } break; 1229 case DM_BC_ESSENTIAL_FIELD: { 1230 PetscPointFunc func_t = (PetscPointFunc)bvfunc; 1231 1232 PetscCall(DMPlexLabelAddCells(dm, label)); 1233 PetscCall(DMPlexInsertBoundaryValuesEssentialField(dm, time, locX, field, Nc, comps, label, numids, ids, func_t, ctx, locX)); 1234 PetscCall(DMPlexLabelClearCells(dm, label)); 1235 } break; 1236 default: 1237 break; 1238 } 1239 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 1240 } 1241 PetscFunctionReturn(PETSC_SUCCESS); 1242 } 1243 1244 /*@ 1245 DMPlexInsertBoundaryValues - Puts coefficients which represent boundary values into the local solution vector 1246 1247 Not Collective 1248 1249 Input Parameters: 1250 + dm - The `DM` 1251 . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions 1252 . time - The time 1253 . faceGeomFVM - Face geometry data for FV discretizations 1254 . cellGeomFVM - Cell geometry data for FV discretizations 1255 - gradFVM - Gradient reconstruction data for FV discretizations 1256 1257 Output Parameter: 1258 . locX - Solution updated with boundary values 1259 1260 Level: intermediate 1261 1262 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()`, `DMAddBoundary()` 1263 @*/ 1264 PetscErrorCode DMPlexInsertBoundaryValues(DM dm, PetscBool insertEssential, Vec locX, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM) 1265 { 1266 PetscFunctionBegin; 1267 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 1268 PetscValidHeaderSpecific(locX, VEC_CLASSID, 3); 1269 if (faceGeomFVM) PetscValidHeaderSpecific(faceGeomFVM, VEC_CLASSID, 5); 1270 if (cellGeomFVM) PetscValidHeaderSpecific(cellGeomFVM, VEC_CLASSID, 6); 1271 if (gradFVM) PetscValidHeaderSpecific(gradFVM, VEC_CLASSID, 7); 1272 PetscTryMethod(dm, "DMPlexInsertBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX, time, faceGeomFVM, cellGeomFVM, gradFVM)); 1273 PetscFunctionReturn(PETSC_SUCCESS); 1274 } 1275 1276 /*@ 1277 DMPlexInsertTimeDerivativeBoundaryValues - Puts coefficients which represent boundary values of the time derivative into the local solution vector 1278 1279 Input Parameters: 1280 + dm - The `DM` 1281 . insertEssential - Should I insert essential (e.g. Dirichlet) or inessential (e.g. Neumann) boundary conditions 1282 . time - The time 1283 . faceGeomFVM - Face geometry data for FV discretizations 1284 . cellGeomFVM - Cell geometry data for FV discretizations 1285 - gradFVM - Gradient reconstruction data for FV discretizations 1286 1287 Output Parameter: 1288 . locX_t - Solution updated with boundary values 1289 1290 Level: developer 1291 1292 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunctionLabelLocal()` 1293 @*/ 1294 PetscErrorCode DMPlexInsertTimeDerivativeBoundaryValues(DM dm, PetscBool insertEssential, Vec locX_t, PetscReal time, Vec faceGeomFVM, Vec cellGeomFVM, Vec gradFVM) 1295 { 1296 PetscFunctionBegin; 1297 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 1298 if (locX_t) PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 3); 1299 if (faceGeomFVM) PetscValidHeaderSpecific(faceGeomFVM, VEC_CLASSID, 5); 1300 if (cellGeomFVM) PetscValidHeaderSpecific(cellGeomFVM, VEC_CLASSID, 6); 1301 if (gradFVM) PetscValidHeaderSpecific(gradFVM, VEC_CLASSID, 7); 1302 PetscTryMethod(dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", (DM, PetscBool, Vec, PetscReal, Vec, Vec, Vec), (dm, insertEssential, locX_t, time, faceGeomFVM, cellGeomFVM, gradFVM)); 1303 PetscFunctionReturn(PETSC_SUCCESS); 1304 } 1305 1306 // Handle non-essential (e.g. outflow) boundary values 1307 PetscErrorCode DMPlexInsertBoundaryValuesFVM(DM dm, PetscFV fv, Vec locX, PetscReal time, Vec *locGradient) 1308 { 1309 DM dmGrad; 1310 Vec cellGeometryFVM, faceGeometryFVM, locGrad = NULL; 1311 1312 PetscFunctionBegin; 1313 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 1314 PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2); 1315 PetscValidHeaderSpecific(locX, VEC_CLASSID, 3); 1316 if (locGradient) { 1317 PetscAssertPointer(locGradient, 5); 1318 *locGradient = NULL; 1319 } 1320 PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL)); 1321 /* Reconstruct and limit cell gradients */ 1322 PetscCall(DMPlexGetGradientDM(dm, fv, &dmGrad)); 1323 if (dmGrad) { 1324 Vec grad; 1325 PetscInt fStart, fEnd; 1326 1327 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 1328 PetscCall(DMGetGlobalVector(dmGrad, &grad)); 1329 PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad)); 1330 /* Communicate gradient values */ 1331 PetscCall(DMGetLocalVector(dmGrad, &locGrad)); 1332 PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad)); 1333 PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad)); 1334 PetscCall(DMRestoreGlobalVector(dmGrad, &grad)); 1335 } 1336 PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, time, faceGeometryFVM, cellGeometryFVM, locGrad)); 1337 if (locGradient) *locGradient = locGrad; 1338 else if (locGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad)); 1339 PetscFunctionReturn(PETSC_SUCCESS); 1340 } 1341 1342 PetscErrorCode DMComputeL2Diff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff) 1343 { 1344 Vec localX; 1345 1346 PetscFunctionBegin; 1347 PetscCall(DMGetLocalVector(dm, &localX)); 1348 PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, localX, time, NULL, NULL, NULL)); 1349 PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX)); 1350 PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX)); 1351 PetscCall(DMPlexComputeL2DiffLocal(dm, time, funcs, ctxs, localX, diff)); 1352 PetscCall(DMRestoreLocalVector(dm, &localX)); 1353 PetscFunctionReturn(PETSC_SUCCESS); 1354 } 1355 1356 /*@C 1357 DMPlexComputeL2DiffLocal - This function computes the L_2 difference between a function u and an FEM interpolant solution u_h. 1358 1359 Collective 1360 1361 Input Parameters: 1362 + dm - The `DM` 1363 . time - The time 1364 . funcs - The functions to evaluate for each field component 1365 . ctxs - Optional array of contexts to pass to each function, or `NULL`. 1366 - localX - The coefficient vector u_h, a local vector 1367 1368 Output Parameter: 1369 . diff - The diff ||u - u_h||_2 1370 1371 Level: developer 1372 1373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2FieldDiff()`, `DMComputeL2GradientDiff()` 1374 @*/ 1375 PetscErrorCode DMPlexComputeL2DiffLocal(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec localX, PetscReal *diff) 1376 { 1377 const PetscInt debug = ((DM_Plex *)dm->data)->printL2; 1378 DM tdm; 1379 Vec tv; 1380 PetscSection section; 1381 PetscQuadrature quad; 1382 PetscFEGeom fegeom; 1383 PetscScalar *funcVal, *interpolant; 1384 PetscReal *coords, *gcoords; 1385 PetscReal localDiff = 0.0; 1386 const PetscReal *quadWeights; 1387 PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cellHeight, cStart, cEnd, c, field, fieldOffset; 1388 PetscBool transform; 1389 1390 PetscFunctionBegin; 1391 PetscCall(DMGetDimension(dm, &dim)); 1392 PetscCall(DMGetCoordinateDim(dm, &coordDim)); 1393 fegeom.dimEmbed = coordDim; 1394 PetscCall(DMGetLocalSection(dm, §ion)); 1395 PetscCall(PetscSectionGetNumFields(section, &numFields)); 1396 PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm)); 1397 PetscCall(DMGetBasisTransformVec_Internal(dm, &tv)); 1398 PetscCall(DMHasBasisTransform(dm, &transform)); 1399 PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!"); 1400 for (field = 0; field < numFields; ++field) { 1401 PetscObject obj; 1402 PetscClassId id; 1403 PetscInt Nc; 1404 1405 PetscCall(DMGetField(dm, field, NULL, &obj)); 1406 PetscCall(PetscObjectGetClassId(obj, &id)); 1407 if (id == PETSCFE_CLASSID) { 1408 PetscFE fe = (PetscFE)obj; 1409 1410 PetscCall(PetscFEGetQuadrature(fe, &quad)); 1411 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 1412 } else if (id == PETSCFV_CLASSID) { 1413 PetscFV fv = (PetscFV)obj; 1414 1415 PetscCall(PetscFVGetQuadrature(fv, &quad)); 1416 PetscCall(PetscFVGetNumComponents(fv, &Nc)); 1417 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 1418 numComponents += Nc; 1419 } 1420 PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights)); 1421 PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents); 1422 PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * (Nq + 1), &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ)); 1423 PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight)); 1424 PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd)); 1425 for (c = cStart; c < cEnd; ++c) { 1426 PetscScalar *x = NULL; 1427 PetscReal elemDiff = 0.0; 1428 PetscInt qc = 0; 1429 1430 PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ)); 1431 PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x)); 1432 1433 for (field = 0, fieldOffset = 0; field < numFields; ++field) { 1434 PetscObject obj; 1435 PetscClassId id; 1436 void *const ctx = ctxs ? ctxs[field] : NULL; 1437 PetscInt Nb, Nc, q, fc; 1438 1439 PetscCall(DMGetField(dm, field, NULL, &obj)); 1440 PetscCall(PetscObjectGetClassId(obj, &id)); 1441 if (id == PETSCFE_CLASSID) { 1442 PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc)); 1443 PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb)); 1444 } else if (id == PETSCFV_CLASSID) { 1445 PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc)); 1446 Nb = 1; 1447 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 1448 if (debug) { 1449 char title[1024]; 1450 PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field)); 1451 PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset])); 1452 } 1453 for (q = 0; q < Nq; ++q) { 1454 PetscFEGeom qgeom; 1455 PetscErrorCode ierr; 1456 1457 qgeom.dimEmbed = fegeom.dimEmbed; 1458 qgeom.J = &fegeom.J[q * coordDim * coordDim]; 1459 qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim]; 1460 qgeom.detJ = &fegeom.detJ[q]; 1461 PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", point %" PetscInt_FMT, (double)fegeom.detJ[q], c, q); 1462 if (transform) { 1463 gcoords = &coords[coordDim * Nq]; 1464 PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx)); 1465 } else { 1466 gcoords = &coords[coordDim * q]; 1467 } 1468 PetscCall(PetscArrayzero(funcVal, Nc)); 1469 ierr = (*funcs[field])(coordDim, time, gcoords, Nc, funcVal, ctx); 1470 if (ierr) { 1471 PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x)); 1472 PetscCall(DMRestoreLocalVector(dm, &localX)); 1473 PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ)); 1474 } 1475 if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx)); 1476 if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant)); 1477 else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant)); 1478 else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 1479 for (fc = 0; fc < Nc; ++fc) { 1480 const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)]; 1481 if (debug) 1482 PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " field %" PetscInt_FMT ",%" PetscInt_FMT " point %g %g %g diff %g (%g, %g)\n", c, field, fc, (double)(coordDim > 0 ? coords[coordDim * q] : 0.), (double)(coordDim > 1 ? coords[coordDim * q + 1] : 0.), (double)(coordDim > 2 ? coords[coordDim * q + 2] : 0.), 1483 (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]), (double)PetscRealPart(interpolant[fc]), (double)PetscRealPart(funcVal[fc]))); 1484 elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]; 1485 } 1486 } 1487 fieldOffset += Nb; 1488 qc += Nc; 1489 } 1490 PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x)); 1491 if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff)); 1492 localDiff += elemDiff; 1493 } 1494 PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ)); 1495 PetscCall(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm))); 1496 *diff = PetscSqrtReal(*diff); 1497 PetscFunctionReturn(PETSC_SUCCESS); 1498 } 1499 1500 PetscErrorCode DMComputeL2GradientDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, const PetscReal n[], PetscReal *diff) 1501 { 1502 const PetscInt debug = ((DM_Plex *)dm->data)->printL2; 1503 DM tdm; 1504 PetscSection section; 1505 PetscQuadrature quad; 1506 Vec localX, tv; 1507 PetscScalar *funcVal, *interpolant; 1508 const PetscReal *quadWeights; 1509 PetscFEGeom fegeom; 1510 PetscReal *coords, *gcoords; 1511 PetscReal localDiff = 0.0; 1512 PetscInt dim, coordDim, qNc = 0, Nq = 0, numFields, numComponents = 0, cStart, cEnd, c, field, fieldOffset; 1513 PetscBool transform; 1514 1515 PetscFunctionBegin; 1516 PetscCall(DMGetDimension(dm, &dim)); 1517 PetscCall(DMGetCoordinateDim(dm, &coordDim)); 1518 fegeom.dimEmbed = coordDim; 1519 PetscCall(DMGetLocalSection(dm, §ion)); 1520 PetscCall(PetscSectionGetNumFields(section, &numFields)); 1521 PetscCall(DMGetLocalVector(dm, &localX)); 1522 PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX)); 1523 PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX)); 1524 PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm)); 1525 PetscCall(DMGetBasisTransformVec_Internal(dm, &tv)); 1526 PetscCall(DMHasBasisTransform(dm, &transform)); 1527 for (field = 0; field < numFields; ++field) { 1528 PetscFE fe; 1529 PetscInt Nc; 1530 1531 PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe)); 1532 PetscCall(PetscFEGetQuadrature(fe, &quad)); 1533 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 1534 numComponents += Nc; 1535 } 1536 PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, NULL, &quadWeights)); 1537 PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents); 1538 /* PetscCall(DMProjectFunctionLocal(dm, fe, funcs, INSERT_BC_VALUES, localX)); */ 1539 PetscCall(PetscMalloc6(numComponents, &funcVal, coordDim * (Nq + 1), &coords, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ, numComponents * coordDim, &interpolant, Nq, &fegeom.detJ)); 1540 PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd)); 1541 for (c = cStart; c < cEnd; ++c) { 1542 PetscScalar *x = NULL; 1543 PetscReal elemDiff = 0.0; 1544 PetscInt qc = 0; 1545 1546 PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ)); 1547 PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x)); 1548 1549 for (field = 0, fieldOffset = 0; field < numFields; ++field) { 1550 PetscFE fe; 1551 void *const ctx = ctxs ? ctxs[field] : NULL; 1552 PetscInt Nb, Nc, q, fc; 1553 1554 PetscCall(DMGetField(dm, field, NULL, (PetscObject *)&fe)); 1555 PetscCall(PetscFEGetDimension(fe, &Nb)); 1556 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 1557 if (debug) { 1558 char title[1024]; 1559 PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, field)); 1560 PetscCall(DMPrintCellVector(c, title, Nb, &x[fieldOffset])); 1561 } 1562 for (q = 0; q < Nq; ++q) { 1563 PetscFEGeom qgeom; 1564 PetscErrorCode ierr; 1565 1566 qgeom.dimEmbed = fegeom.dimEmbed; 1567 qgeom.J = &fegeom.J[q * coordDim * coordDim]; 1568 qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim]; 1569 qgeom.detJ = &fegeom.detJ[q]; 1570 PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], c, q); 1571 if (transform) { 1572 gcoords = &coords[coordDim * Nq]; 1573 PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[coordDim * q], PETSC_TRUE, coordDim, &coords[coordDim * q], gcoords, dm->transformCtx)); 1574 } else { 1575 gcoords = &coords[coordDim * q]; 1576 } 1577 PetscCall(PetscArrayzero(funcVal, Nc)); 1578 ierr = (*funcs[field])(coordDim, time, gcoords, n, Nc, funcVal, ctx); 1579 if (ierr) { 1580 PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x)); 1581 PetscCall(DMRestoreLocalVector(dm, &localX)); 1582 PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ)); 1583 } 1584 if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[coordDim * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx)); 1585 PetscCall(PetscFEInterpolateGradient_Static(fe, 1, &x[fieldOffset], &qgeom, q, interpolant)); 1586 /* Overwrite with the dot product if the normal is given */ 1587 if (n) { 1588 for (fc = 0; fc < Nc; ++fc) { 1589 PetscScalar sum = 0.0; 1590 PetscInt d; 1591 for (d = 0; d < dim; ++d) sum += interpolant[fc * dim + d] * n[d]; 1592 interpolant[fc] = sum; 1593 } 1594 } 1595 for (fc = 0; fc < Nc; ++fc) { 1596 const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)]; 1597 if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " fieldDer %" PetscInt_FMT ",%" PetscInt_FMT " diff %g\n", c, field, fc, (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]))); 1598 elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]; 1599 } 1600 } 1601 fieldOffset += Nb; 1602 qc += Nc; 1603 } 1604 PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x)); 1605 if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " elem %" PetscInt_FMT " diff %g\n", c, (double)elemDiff)); 1606 localDiff += elemDiff; 1607 } 1608 PetscCall(PetscFree6(funcVal, coords, fegeom.J, fegeom.invJ, interpolant, fegeom.detJ)); 1609 PetscCall(DMRestoreLocalVector(dm, &localX)); 1610 PetscCall(MPIU_Allreduce(&localDiff, diff, 1, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm))); 1611 *diff = PetscSqrtReal(*diff); 1612 PetscFunctionReturn(PETSC_SUCCESS); 1613 } 1614 1615 PetscErrorCode DMComputeL2FieldDiff_Plex(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, PetscReal *diff) 1616 { 1617 const PetscInt debug = ((DM_Plex *)dm->data)->printL2; 1618 DM tdm; 1619 DMLabel depthLabel; 1620 PetscSection section; 1621 Vec localX, tv; 1622 PetscReal *localDiff; 1623 PetscInt dim, depth, dE, Nf, f, Nds, s; 1624 PetscBool transform; 1625 1626 PetscFunctionBegin; 1627 PetscCall(DMGetDimension(dm, &dim)); 1628 PetscCall(DMGetCoordinateDim(dm, &dE)); 1629 PetscCall(DMGetLocalSection(dm, §ion)); 1630 PetscCall(DMGetLocalVector(dm, &localX)); 1631 PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm)); 1632 PetscCall(DMGetBasisTransformVec_Internal(dm, &tv)); 1633 PetscCall(DMHasBasisTransform(dm, &transform)); 1634 PetscCall(DMGetNumFields(dm, &Nf)); 1635 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 1636 PetscCall(DMLabelGetNumValues(depthLabel, &depth)); 1637 1638 PetscCall(VecSet(localX, 0.0)); 1639 PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX)); 1640 PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX)); 1641 PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX)); 1642 PetscCall(DMGetNumDS(dm, &Nds)); 1643 PetscCall(PetscCalloc1(Nf, &localDiff)); 1644 for (s = 0; s < Nds; ++s) { 1645 PetscDS ds; 1646 DMLabel label; 1647 IS fieldIS, pointIS; 1648 const PetscInt *fields, *points = NULL; 1649 PetscQuadrature quad; 1650 const PetscReal *quadPoints, *quadWeights; 1651 PetscFEGeom fegeom; 1652 PetscReal *coords, *gcoords; 1653 PetscScalar *funcVal, *interpolant; 1654 PetscBool isCohesive; 1655 PetscInt qNc, Nq, totNc, cStart = 0, cEnd, c, dsNf; 1656 1657 PetscCall(DMGetRegionNumDS(dm, s, &label, &fieldIS, &ds, NULL)); 1658 PetscCall(ISGetIndices(fieldIS, &fields)); 1659 PetscCall(PetscDSIsCohesive(ds, &isCohesive)); 1660 PetscCall(PetscDSGetNumFields(ds, &dsNf)); 1661 PetscCall(PetscDSGetTotalComponents(ds, &totNc)); 1662 PetscCall(PetscDSGetQuadrature(ds, &quad)); 1663 PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights)); 1664 PetscCheck(!(qNc != 1) || !(qNc != totNc), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, totNc); 1665 PetscCall(PetscCalloc6(totNc, &funcVal, totNc, &interpolant, dE * (Nq + 1), &coords, Nq, &fegeom.detJ, dE * dE * Nq, &fegeom.J, dE * dE * Nq, &fegeom.invJ)); 1666 if (!label) { 1667 PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd)); 1668 } else { 1669 PetscCall(DMLabelGetStratumIS(label, 1, &pointIS)); 1670 PetscCall(ISGetLocalSize(pointIS, &cEnd)); 1671 PetscCall(ISGetIndices(pointIS, &points)); 1672 } 1673 for (c = cStart; c < cEnd; ++c) { 1674 const PetscInt cell = points ? points[c] : c; 1675 PetscScalar *x = NULL; 1676 const PetscInt *cone; 1677 PetscInt qc = 0, fOff = 0, dep; 1678 1679 PetscCall(DMLabelGetValue(depthLabel, cell, &dep)); 1680 if (dep != depth - 1) continue; 1681 if (isCohesive) { 1682 PetscCall(DMPlexGetCone(dm, cell, &cone)); 1683 PetscCall(DMPlexComputeCellGeometryFEM(dm, cone[0], quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ)); 1684 } else { 1685 PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ)); 1686 } 1687 PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, cell, 0, NULL, &x)); 1688 for (f = 0; f < dsNf; ++f) { 1689 PetscObject obj; 1690 PetscClassId id; 1691 void *const ctx = ctxs ? ctxs[fields[f]] : NULL; 1692 PetscInt Nb, Nc, q, fc; 1693 PetscReal elemDiff = 0.0; 1694 PetscBool cohesive; 1695 1696 PetscCall(PetscDSGetCohesive(ds, f, &cohesive)); 1697 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 1698 PetscCall(PetscObjectGetClassId(obj, &id)); 1699 if (id == PETSCFE_CLASSID) { 1700 PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc)); 1701 PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb)); 1702 } else if (id == PETSCFV_CLASSID) { 1703 PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc)); 1704 Nb = 1; 1705 } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]); 1706 if (isCohesive && !cohesive) { 1707 fOff += Nb * 2; 1708 qc += Nc; 1709 continue; 1710 } 1711 if (debug) { 1712 char title[1024]; 1713 PetscCall(PetscSNPrintf(title, 1023, "Solution for Field %" PetscInt_FMT, fields[f])); 1714 PetscCall(DMPrintCellVector(cell, title, Nb, &x[fOff])); 1715 } 1716 for (q = 0; q < Nq; ++q) { 1717 PetscFEGeom qgeom; 1718 PetscErrorCode ierr; 1719 1720 qgeom.dimEmbed = fegeom.dimEmbed; 1721 qgeom.J = &fegeom.J[q * dE * dE]; 1722 qgeom.invJ = &fegeom.invJ[q * dE * dE]; 1723 qgeom.detJ = &fegeom.detJ[q]; 1724 PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for cell %" PetscInt_FMT ", quadrature point %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q); 1725 if (transform) { 1726 gcoords = &coords[dE * Nq]; 1727 PetscCall(DMPlexBasisTransformApplyReal_Internal(dm, &coords[dE * q], PETSC_TRUE, dE, &coords[dE * q], gcoords, dm->transformCtx)); 1728 } else { 1729 gcoords = &coords[dE * q]; 1730 } 1731 for (fc = 0; fc < Nc; ++fc) funcVal[fc] = 0.; 1732 ierr = (*funcs[fields[f]])(dE, time, gcoords, Nc, funcVal, ctx); 1733 if (ierr) { 1734 PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x)); 1735 PetscCall(DMRestoreLocalVector(dm, &localX)); 1736 PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ)); 1737 } 1738 if (transform) PetscCall(DMPlexBasisTransformApply_Internal(dm, &coords[dE * q], PETSC_FALSE, Nc, funcVal, funcVal, dm->transformCtx)); 1739 /* Call once for each face, except for lagrange field */ 1740 if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fOff], &qgeom, q, interpolant)); 1741 else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fOff], q, interpolant)); 1742 else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, fields[f]); 1743 for (fc = 0; fc < Nc; ++fc) { 1744 const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)]; 1745 if (debug) 1746 PetscCall(PetscPrintf(PETSC_COMM_SELF, " cell %" PetscInt_FMT " field %" PetscInt_FMT ",%" PetscInt_FMT " point %g %g %g diff %g\n", cell, fields[f], fc, (double)(dE > 0 ? coords[dE * q] : 0.), (double)(dE > 1 ? coords[dE * q + 1] : 0.), (double)(dE > 2 ? coords[dE * q + 2] : 0.), 1747 (double)(PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]))); 1748 elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]; 1749 } 1750 } 1751 fOff += Nb; 1752 qc += Nc; 1753 localDiff[fields[f]] += elemDiff; 1754 if (debug) PetscCall(PetscPrintf(PETSC_COMM_SELF, " cell %" PetscInt_FMT " field %" PetscInt_FMT " cum diff %g\n", cell, fields[f], (double)localDiff[fields[f]])); 1755 } 1756 PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, cell, NULL, &x)); 1757 } 1758 if (label) { 1759 PetscCall(ISRestoreIndices(pointIS, &points)); 1760 PetscCall(ISDestroy(&pointIS)); 1761 } 1762 PetscCall(ISRestoreIndices(fieldIS, &fields)); 1763 PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ)); 1764 } 1765 PetscCall(DMRestoreLocalVector(dm, &localX)); 1766 PetscCall(MPIU_Allreduce(localDiff, diff, Nf, MPIU_REAL, MPIU_SUM, PetscObjectComm((PetscObject)dm))); 1767 PetscCall(PetscFree(localDiff)); 1768 for (f = 0; f < Nf; ++f) diff[f] = PetscSqrtReal(diff[f]); 1769 PetscFunctionReturn(PETSC_SUCCESS); 1770 } 1771 1772 /*@C 1773 DMPlexComputeL2DiffVec - This function computes the cellwise L_2 difference between a function u and an FEM interpolant solution u_h, and stores it in a Vec. 1774 1775 Collective 1776 1777 Input Parameters: 1778 + dm - The `DM` 1779 . time - The time 1780 . funcs - The functions to evaluate for each field component: `NULL` means that component does not contribute to error calculation 1781 . ctxs - Optional array of contexts to pass to each function, or `NULL`. 1782 - X - The coefficient vector u_h 1783 1784 Output Parameter: 1785 . D - A `Vec` which holds the difference ||u - u_h||_2 for each cell 1786 1787 Level: developer 1788 1789 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()` 1790 @*/ 1791 PetscErrorCode DMPlexComputeL2DiffVec(DM dm, PetscReal time, PetscErrorCode (**funcs)(PetscInt, PetscReal, const PetscReal[], PetscInt, PetscScalar *, void *), void **ctxs, Vec X, Vec D) 1792 { 1793 PetscSection section; 1794 PetscQuadrature quad; 1795 Vec localX; 1796 PetscFEGeom fegeom; 1797 PetscScalar *funcVal, *interpolant; 1798 PetscReal *coords; 1799 const PetscReal *quadPoints, *quadWeights; 1800 PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, c, field, fieldOffset; 1801 1802 PetscFunctionBegin; 1803 PetscCall(VecSet(D, 0.0)); 1804 PetscCall(DMGetDimension(dm, &dim)); 1805 PetscCall(DMGetCoordinateDim(dm, &coordDim)); 1806 PetscCall(DMGetLocalSection(dm, §ion)); 1807 PetscCall(PetscSectionGetNumFields(section, &numFields)); 1808 PetscCall(DMGetLocalVector(dm, &localX)); 1809 PetscCall(DMProjectFunctionLocal(dm, time, funcs, ctxs, INSERT_BC_VALUES, localX)); 1810 PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, localX)); 1811 PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, localX)); 1812 for (field = 0; field < numFields; ++field) { 1813 PetscObject obj; 1814 PetscClassId id; 1815 PetscInt Nc; 1816 1817 PetscCall(DMGetField(dm, field, NULL, &obj)); 1818 PetscCall(PetscObjectGetClassId(obj, &id)); 1819 if (id == PETSCFE_CLASSID) { 1820 PetscFE fe = (PetscFE)obj; 1821 1822 PetscCall(PetscFEGetQuadrature(fe, &quad)); 1823 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 1824 } else if (id == PETSCFV_CLASSID) { 1825 PetscFV fv = (PetscFV)obj; 1826 1827 PetscCall(PetscFVGetQuadrature(fv, &quad)); 1828 PetscCall(PetscFVGetNumComponents(fv, &Nc)); 1829 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 1830 numComponents += Nc; 1831 } 1832 PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights)); 1833 PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents); 1834 PetscCall(PetscMalloc6(numComponents, &funcVal, numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ)); 1835 PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd)); 1836 for (c = cStart; c < cEnd; ++c) { 1837 PetscScalar *x = NULL; 1838 PetscScalar elemDiff = 0.0; 1839 PetscInt qc = 0; 1840 1841 PetscCall(DMPlexComputeCellGeometryFEM(dm, c, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ)); 1842 PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, NULL, PETSC_FALSE, localX, c, 0, NULL, &x)); 1843 1844 for (field = 0, fieldOffset = 0; field < numFields; ++field) { 1845 PetscObject obj; 1846 PetscClassId id; 1847 void *const ctx = ctxs ? ctxs[field] : NULL; 1848 PetscInt Nb, Nc, q, fc; 1849 1850 PetscCall(DMGetField(dm, field, NULL, &obj)); 1851 PetscCall(PetscObjectGetClassId(obj, &id)); 1852 if (id == PETSCFE_CLASSID) { 1853 PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc)); 1854 PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb)); 1855 } else if (id == PETSCFV_CLASSID) { 1856 PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc)); 1857 Nb = 1; 1858 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 1859 if (funcs[field]) { 1860 for (q = 0; q < Nq; ++q) { 1861 PetscFEGeom qgeom; 1862 1863 qgeom.dimEmbed = fegeom.dimEmbed; 1864 qgeom.J = &fegeom.J[q * coordDim * coordDim]; 1865 qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim]; 1866 qgeom.detJ = &fegeom.detJ[q]; 1867 PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], c, q); 1868 PetscCall((*funcs[field])(coordDim, time, &coords[q * coordDim], Nc, funcVal, ctx)); 1869 #if defined(needs_fix_with_return_code_argument) 1870 if (ierr) { 1871 PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x)); 1872 PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ)); 1873 PetscCall(DMRestoreLocalVector(dm, &localX)); 1874 } 1875 #endif 1876 if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[fieldOffset], &qgeom, q, interpolant)); 1877 else if (id == PETSCFV_CLASSID) PetscCall(PetscFVInterpolate_Static((PetscFV)obj, &x[fieldOffset], q, interpolant)); 1878 else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 1879 for (fc = 0; fc < Nc; ++fc) { 1880 const PetscReal wt = quadWeights[q * qNc + (qNc == 1 ? 0 : qc + fc)]; 1881 elemDiff += PetscSqr(PetscRealPart(interpolant[fc] - funcVal[fc])) * wt * fegeom.detJ[q]; 1882 } 1883 } 1884 } 1885 fieldOffset += Nb; 1886 qc += Nc; 1887 } 1888 PetscCall(DMPlexVecRestoreClosure(dm, NULL, localX, c, NULL, &x)); 1889 PetscCall(VecSetValue(D, c - cStart, elemDiff, INSERT_VALUES)); 1890 } 1891 PetscCall(PetscFree6(funcVal, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ)); 1892 PetscCall(DMRestoreLocalVector(dm, &localX)); 1893 PetscCall(VecSqrtAbs(D)); 1894 PetscFunctionReturn(PETSC_SUCCESS); 1895 } 1896 1897 /*@ 1898 DMPlexComputeClementInterpolant - This function computes the L2 projection of the cellwise values of a function u onto P1 1899 1900 Collective 1901 1902 Input Parameters: 1903 + dm - The `DM` 1904 - locX - The coefficient vector u_h 1905 1906 Output Parameter: 1907 . locC - A `Vec` which holds the Clement interpolant of the function 1908 1909 Level: developer 1910 1911 Note: 1912 $ u_h(v_i) = \sum_{T_i \in support(v_i)} |T_i| u_h(T_i) / \sum_{T_i \in support(v_i)} |T_i| $ where $ |T_i| $ is the cell volume 1913 1914 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()` 1915 @*/ 1916 PetscErrorCode DMPlexComputeClementInterpolant(DM dm, Vec locX, Vec locC) 1917 { 1918 PetscInt debug = ((DM_Plex *)dm->data)->printFEM; 1919 DM dmc; 1920 PetscQuadrature quad; 1921 PetscScalar *interpolant, *valsum; 1922 PetscFEGeom fegeom; 1923 PetscReal *coords; 1924 const PetscReal *quadPoints, *quadWeights; 1925 PetscInt dim, cdim, Nf, f, Nc = 0, Nq, qNc, cStart, cEnd, vStart, vEnd, v; 1926 1927 PetscFunctionBegin; 1928 PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite)); 1929 PetscCall(VecGetDM(locC, &dmc)); 1930 PetscCall(VecSet(locC, 0.0)); 1931 PetscCall(DMGetDimension(dm, &dim)); 1932 PetscCall(DMGetCoordinateDim(dm, &cdim)); 1933 fegeom.dimEmbed = cdim; 1934 PetscCall(DMGetNumFields(dm, &Nf)); 1935 PetscCheck(Nf > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!"); 1936 for (f = 0; f < Nf; ++f) { 1937 PetscObject obj; 1938 PetscClassId id; 1939 PetscInt fNc; 1940 1941 PetscCall(DMGetField(dm, f, NULL, &obj)); 1942 PetscCall(PetscObjectGetClassId(obj, &id)); 1943 if (id == PETSCFE_CLASSID) { 1944 PetscFE fe = (PetscFE)obj; 1945 1946 PetscCall(PetscFEGetQuadrature(fe, &quad)); 1947 PetscCall(PetscFEGetNumComponents(fe, &fNc)); 1948 } else if (id == PETSCFV_CLASSID) { 1949 PetscFV fv = (PetscFV)obj; 1950 1951 PetscCall(PetscFVGetQuadrature(fv, &quad)); 1952 PetscCall(PetscFVGetNumComponents(fv, &fNc)); 1953 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f); 1954 Nc += fNc; 1955 } 1956 PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights)); 1957 PetscCheck(qNc == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " > 1", qNc); 1958 PetscCall(PetscMalloc6(Nc * 2, &valsum, Nc, &interpolant, cdim * Nq, &coords, Nq, &fegeom.detJ, cdim * cdim * Nq, &fegeom.J, cdim * cdim * Nq, &fegeom.invJ)); 1959 PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 1960 PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd)); 1961 for (v = vStart; v < vEnd; ++v) { 1962 PetscScalar volsum = 0.0; 1963 PetscInt *star = NULL; 1964 PetscInt starSize, st, fc; 1965 1966 PetscCall(PetscArrayzero(valsum, Nc)); 1967 PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star)); 1968 for (st = 0; st < starSize * 2; st += 2) { 1969 const PetscInt cell = star[st]; 1970 PetscScalar *val = &valsum[Nc]; 1971 PetscScalar *x = NULL; 1972 PetscReal vol = 0.0; 1973 PetscInt foff = 0; 1974 1975 if ((cell < cStart) || (cell >= cEnd)) continue; 1976 PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ)); 1977 PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x)); 1978 for (f = 0; f < Nf; ++f) { 1979 PetscObject obj; 1980 PetscClassId id; 1981 PetscInt Nb, fNc, q; 1982 1983 PetscCall(PetscArrayzero(val, Nc)); 1984 PetscCall(DMGetField(dm, f, NULL, &obj)); 1985 PetscCall(PetscObjectGetClassId(obj, &id)); 1986 if (id == PETSCFE_CLASSID) { 1987 PetscCall(PetscFEGetNumComponents((PetscFE)obj, &fNc)); 1988 PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb)); 1989 } else if (id == PETSCFV_CLASSID) { 1990 PetscCall(PetscFVGetNumComponents((PetscFV)obj, &fNc)); 1991 Nb = 1; 1992 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f); 1993 for (q = 0; q < Nq; ++q) { 1994 const PetscReal wt = quadWeights[q] * fegeom.detJ[q]; 1995 PetscFEGeom qgeom; 1996 1997 qgeom.dimEmbed = fegeom.dimEmbed; 1998 qgeom.J = &fegeom.J[q * cdim * cdim]; 1999 qgeom.invJ = &fegeom.invJ[q * cdim * cdim]; 2000 qgeom.detJ = &fegeom.detJ[q]; 2001 PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q); 2002 if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolate_Static((PetscFE)obj, &x[foff], &qgeom, q, interpolant)); 2003 else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f); 2004 for (fc = 0; fc < fNc; ++fc) val[foff + fc] += interpolant[fc] * wt; 2005 vol += wt; 2006 } 2007 foff += Nb; 2008 } 2009 PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x)); 2010 for (fc = 0; fc < Nc; ++fc) valsum[fc] += val[fc]; 2011 volsum += vol; 2012 if (debug) { 2013 PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " value: [", v, cell)); 2014 for (fc = 0; fc < Nc; ++fc) { 2015 if (fc) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", ")); 2016 PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(val[fc]))); 2017 } 2018 PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n")); 2019 } 2020 } 2021 for (fc = 0; fc < Nc; ++fc) valsum[fc] /= volsum; 2022 PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star)); 2023 PetscCall(DMPlexVecSetClosure(dmc, NULL, locC, v, valsum, INSERT_VALUES)); 2024 } 2025 PetscCall(PetscFree6(valsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ)); 2026 PetscFunctionReturn(PETSC_SUCCESS); 2027 } 2028 2029 /*@ 2030 DMPlexComputeGradientClementInterpolant - This function computes the L2 projection of the cellwise gradient of a function u onto P1 2031 2032 Collective 2033 2034 Input Parameters: 2035 + dm - The `DM` 2036 - locX - The coefficient vector u_h 2037 2038 Output Parameter: 2039 . locC - A `Vec` which holds the Clement interpolant of the gradient 2040 2041 Level: developer 2042 2043 Note: 2044 $\nabla u_h(v_i) = \sum_{T_i \in support(v_i)} |T_i| \nabla u_h(T_i) / \sum_{T_i \in support(v_i)} |T_i| $ where $ |T_i| $ is the cell volume 2045 2046 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMProjectFunction()`, `DMComputeL2Diff()`, `DMPlexComputeL2FieldDiff()`, `DMComputeL2GradientDiff()` 2047 @*/ 2048 PetscErrorCode DMPlexComputeGradientClementInterpolant(DM dm, Vec locX, Vec locC) 2049 { 2050 DM_Plex *mesh = (DM_Plex *)dm->data; 2051 PetscInt debug = mesh->printFEM; 2052 DM dmC; 2053 PetscQuadrature quad; 2054 PetscScalar *interpolant, *gradsum; 2055 PetscFEGeom fegeom; 2056 PetscReal *coords; 2057 const PetscReal *quadPoints, *quadWeights; 2058 PetscInt dim, coordDim, numFields, numComponents = 0, qNc, Nq, cStart, cEnd, vStart, vEnd, v, field, fieldOffset; 2059 2060 PetscFunctionBegin; 2061 PetscCall(PetscCitationsRegister(ClementCitation, &Clementcite)); 2062 PetscCall(VecGetDM(locC, &dmC)); 2063 PetscCall(VecSet(locC, 0.0)); 2064 PetscCall(DMGetDimension(dm, &dim)); 2065 PetscCall(DMGetCoordinateDim(dm, &coordDim)); 2066 fegeom.dimEmbed = coordDim; 2067 PetscCall(DMGetNumFields(dm, &numFields)); 2068 PetscCheck(numFields, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fields is zero!"); 2069 for (field = 0; field < numFields; ++field) { 2070 PetscObject obj; 2071 PetscClassId id; 2072 PetscInt Nc; 2073 2074 PetscCall(DMGetField(dm, field, NULL, &obj)); 2075 PetscCall(PetscObjectGetClassId(obj, &id)); 2076 if (id == PETSCFE_CLASSID) { 2077 PetscFE fe = (PetscFE)obj; 2078 2079 PetscCall(PetscFEGetQuadrature(fe, &quad)); 2080 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 2081 } else if (id == PETSCFV_CLASSID) { 2082 PetscFV fv = (PetscFV)obj; 2083 2084 PetscCall(PetscFVGetQuadrature(fv, &quad)); 2085 PetscCall(PetscFVGetNumComponents(fv, &Nc)); 2086 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 2087 numComponents += Nc; 2088 } 2089 PetscCall(PetscQuadratureGetData(quad, NULL, &qNc, &Nq, &quadPoints, &quadWeights)); 2090 PetscCheck(!(qNc != 1) || !(qNc != numComponents), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Quadrature components %" PetscInt_FMT " != %" PetscInt_FMT " field components", qNc, numComponents); 2091 PetscCall(PetscMalloc6(coordDim * numComponents * 2, &gradsum, coordDim * numComponents, &interpolant, coordDim * Nq, &coords, Nq, &fegeom.detJ, coordDim * coordDim * Nq, &fegeom.J, coordDim * coordDim * Nq, &fegeom.invJ)); 2092 PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd)); 2093 PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd)); 2094 for (v = vStart; v < vEnd; ++v) { 2095 PetscScalar volsum = 0.0; 2096 PetscInt *star = NULL; 2097 PetscInt starSize, st, d, fc; 2098 2099 PetscCall(PetscArrayzero(gradsum, coordDim * numComponents)); 2100 PetscCall(DMPlexGetTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star)); 2101 for (st = 0; st < starSize * 2; st += 2) { 2102 const PetscInt cell = star[st]; 2103 PetscScalar *grad = &gradsum[coordDim * numComponents]; 2104 PetscScalar *x = NULL; 2105 PetscReal vol = 0.0; 2106 2107 if ((cell < cStart) || (cell >= cEnd)) continue; 2108 PetscCall(DMPlexComputeCellGeometryFEM(dm, cell, quad, coords, fegeom.J, fegeom.invJ, fegeom.detJ)); 2109 PetscCall(DMPlexVecGetClosure(dm, NULL, locX, cell, NULL, &x)); 2110 for (field = 0, fieldOffset = 0; field < numFields; ++field) { 2111 PetscObject obj; 2112 PetscClassId id; 2113 PetscInt Nb, Nc, q, qc = 0; 2114 2115 PetscCall(PetscArrayzero(grad, coordDim * numComponents)); 2116 PetscCall(DMGetField(dm, field, NULL, &obj)); 2117 PetscCall(PetscObjectGetClassId(obj, &id)); 2118 if (id == PETSCFE_CLASSID) { 2119 PetscCall(PetscFEGetNumComponents((PetscFE)obj, &Nc)); 2120 PetscCall(PetscFEGetDimension((PetscFE)obj, &Nb)); 2121 } else if (id == PETSCFV_CLASSID) { 2122 PetscCall(PetscFVGetNumComponents((PetscFV)obj, &Nc)); 2123 Nb = 1; 2124 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 2125 for (q = 0; q < Nq; ++q) { 2126 PetscFEGeom qgeom; 2127 2128 qgeom.dimEmbed = fegeom.dimEmbed; 2129 qgeom.J = &fegeom.J[q * coordDim * coordDim]; 2130 qgeom.invJ = &fegeom.invJ[q * coordDim * coordDim]; 2131 qgeom.detJ = &fegeom.detJ[q]; 2132 PetscCheck(fegeom.detJ[q] > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Invalid determinant %g for element %" PetscInt_FMT ", quadrature points %" PetscInt_FMT, (double)fegeom.detJ[q], cell, q); 2133 if (id == PETSCFE_CLASSID) PetscCall(PetscFEInterpolateGradient_Static((PetscFE)obj, 1, &x[fieldOffset], &qgeom, q, interpolant)); 2134 else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 2135 for (fc = 0; fc < Nc; ++fc) { 2136 const PetscReal wt = quadWeights[q * qNc + qc]; 2137 2138 for (d = 0; d < coordDim; ++d) grad[fc * coordDim + d] += interpolant[fc * dim + d] * wt * fegeom.detJ[q]; 2139 } 2140 vol += quadWeights[q * qNc] * fegeom.detJ[q]; 2141 } 2142 fieldOffset += Nb; 2143 qc += Nc; 2144 } 2145 PetscCall(DMPlexVecRestoreClosure(dm, NULL, locX, cell, NULL, &x)); 2146 for (fc = 0; fc < numComponents; ++fc) { 2147 for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] += grad[fc * coordDim + d]; 2148 } 2149 volsum += vol; 2150 if (debug) { 2151 PetscCall(PetscPrintf(PETSC_COMM_SELF, "Vertex %" PetscInt_FMT " Cell %" PetscInt_FMT " gradient: [", v, cell)); 2152 for (fc = 0; fc < numComponents; ++fc) { 2153 for (d = 0; d < coordDim; ++d) { 2154 if (fc || d > 0) PetscCall(PetscPrintf(PETSC_COMM_SELF, ", ")); 2155 PetscCall(PetscPrintf(PETSC_COMM_SELF, "%g", (double)PetscRealPart(grad[fc * coordDim + d]))); 2156 } 2157 } 2158 PetscCall(PetscPrintf(PETSC_COMM_SELF, "]\n")); 2159 } 2160 } 2161 for (fc = 0; fc < numComponents; ++fc) { 2162 for (d = 0; d < coordDim; ++d) gradsum[fc * coordDim + d] /= volsum; 2163 } 2164 PetscCall(DMPlexRestoreTransitiveClosure(dm, v, PETSC_FALSE, &starSize, &star)); 2165 PetscCall(DMPlexVecSetClosure(dmC, NULL, locC, v, gradsum, INSERT_VALUES)); 2166 } 2167 PetscCall(PetscFree6(gradsum, interpolant, coords, fegeom.detJ, fegeom.J, fegeom.invJ)); 2168 PetscFunctionReturn(PETSC_SUCCESS); 2169 } 2170 2171 PetscErrorCode DMPlexComputeIntegral_Internal(DM dm, Vec locX, PetscInt cStart, PetscInt cEnd, PetscScalar *cintegral, void *user) 2172 { 2173 DM dmAux = NULL; 2174 PetscDS prob, probAux = NULL; 2175 PetscSection section, sectionAux; 2176 Vec locA; 2177 PetscInt dim, numCells = cEnd - cStart, c, f; 2178 PetscBool useFVM = PETSC_FALSE; 2179 /* DS */ 2180 PetscInt Nf, totDim, *uOff, *uOff_x, numConstants; 2181 PetscInt NfAux, totDimAux, *aOff; 2182 PetscScalar *u, *a; 2183 const PetscScalar *constants; 2184 /* Geometry */ 2185 PetscFEGeom *cgeomFEM; 2186 DM dmGrad; 2187 PetscQuadrature affineQuad = NULL; 2188 Vec cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL; 2189 PetscFVCellGeom *cgeomFVM; 2190 const PetscScalar *lgrad; 2191 PetscInt maxDegree; 2192 DMField coordField; 2193 IS cellIS; 2194 2195 PetscFunctionBegin; 2196 PetscCall(DMGetDS(dm, &prob)); 2197 PetscCall(DMGetDimension(dm, &dim)); 2198 PetscCall(DMGetLocalSection(dm, §ion)); 2199 PetscCall(DMGetNumFields(dm, &Nf)); 2200 /* Determine which discretizations we have */ 2201 for (f = 0; f < Nf; ++f) { 2202 PetscObject obj; 2203 PetscClassId id; 2204 2205 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 2206 PetscCall(PetscObjectGetClassId(obj, &id)); 2207 if (id == PETSCFV_CLASSID) useFVM = PETSC_TRUE; 2208 } 2209 /* Read DS information */ 2210 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 2211 PetscCall(PetscDSGetComponentOffsets(prob, &uOff)); 2212 PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x)); 2213 PetscCall(ISCreateStride(PETSC_COMM_SELF, numCells, cStart, 1, &cellIS)); 2214 PetscCall(PetscDSGetConstants(prob, &numConstants, &constants)); 2215 /* Read Auxiliary DS information */ 2216 PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA)); 2217 if (locA) { 2218 PetscCall(VecGetDM(locA, &dmAux)); 2219 PetscCall(DMGetDS(dmAux, &probAux)); 2220 PetscCall(PetscDSGetNumFields(probAux, &NfAux)); 2221 PetscCall(DMGetLocalSection(dmAux, §ionAux)); 2222 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 2223 PetscCall(PetscDSGetComponentOffsets(probAux, &aOff)); 2224 } 2225 /* Allocate data arrays */ 2226 PetscCall(PetscCalloc1(numCells * totDim, &u)); 2227 if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a)); 2228 /* Read out geometry */ 2229 PetscCall(DMGetCoordinateField(dm, &coordField)); 2230 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 2231 if (maxDegree <= 1) { 2232 PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad)); 2233 if (affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &cgeomFEM)); 2234 } 2235 if (useFVM) { 2236 PetscFV fv = NULL; 2237 Vec grad; 2238 PetscInt fStart, fEnd; 2239 PetscBool compGrad; 2240 2241 for (f = 0; f < Nf; ++f) { 2242 PetscObject obj; 2243 PetscClassId id; 2244 2245 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 2246 PetscCall(PetscObjectGetClassId(obj, &id)); 2247 if (id == PETSCFV_CLASSID) { 2248 fv = (PetscFV)obj; 2249 break; 2250 } 2251 } 2252 PetscCall(PetscFVGetComputeGradients(fv, &compGrad)); 2253 PetscCall(PetscFVSetComputeGradients(fv, PETSC_TRUE)); 2254 PetscCall(DMPlexComputeGeometryFVM(dm, &cellGeometryFVM, &faceGeometryFVM)); 2255 PetscCall(DMPlexComputeGradientFVM(dm, fv, faceGeometryFVM, cellGeometryFVM, &dmGrad)); 2256 PetscCall(PetscFVSetComputeGradients(fv, compGrad)); 2257 PetscCall(VecGetArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM)); 2258 /* Reconstruct and limit cell gradients */ 2259 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 2260 PetscCall(DMGetGlobalVector(dmGrad, &grad)); 2261 PetscCall(DMPlexReconstructGradients_Internal(dm, fv, fStart, fEnd, faceGeometryFVM, cellGeometryFVM, locX, grad)); 2262 /* Communicate gradient values */ 2263 PetscCall(DMGetLocalVector(dmGrad, &locGrad)); 2264 PetscCall(DMGlobalToLocalBegin(dmGrad, grad, INSERT_VALUES, locGrad)); 2265 PetscCall(DMGlobalToLocalEnd(dmGrad, grad, INSERT_VALUES, locGrad)); 2266 PetscCall(DMRestoreGlobalVector(dmGrad, &grad)); 2267 /* Handle non-essential (e.g. outflow) boundary values */ 2268 PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_FALSE, locX, 0.0, faceGeometryFVM, cellGeometryFVM, locGrad)); 2269 PetscCall(VecGetArrayRead(locGrad, &lgrad)); 2270 } 2271 /* Read out data from inputs */ 2272 for (c = cStart; c < cEnd; ++c) { 2273 PetscScalar *x = NULL; 2274 PetscInt i; 2275 2276 PetscCall(DMPlexVecGetClosure(dm, section, locX, c, NULL, &x)); 2277 for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i]; 2278 PetscCall(DMPlexVecRestoreClosure(dm, section, locX, c, NULL, &x)); 2279 if (dmAux) { 2280 PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, locA, c, NULL, &x)); 2281 for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i]; 2282 PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, locA, c, NULL, &x)); 2283 } 2284 } 2285 /* Do integration for each field */ 2286 for (f = 0; f < Nf; ++f) { 2287 PetscObject obj; 2288 PetscClassId id; 2289 PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset; 2290 2291 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 2292 PetscCall(PetscObjectGetClassId(obj, &id)); 2293 if (id == PETSCFE_CLASSID) { 2294 PetscFE fe = (PetscFE)obj; 2295 PetscQuadrature q; 2296 PetscFEGeom *chunkGeom = NULL; 2297 PetscInt Nq, Nb; 2298 2299 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 2300 PetscCall(PetscFEGetQuadrature(fe, &q)); 2301 PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL)); 2302 PetscCall(PetscFEGetDimension(fe, &Nb)); 2303 blockSize = Nb * Nq; 2304 batchSize = numBlocks * blockSize; 2305 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 2306 numChunks = numCells / (numBatches * batchSize); 2307 Ne = numChunks * numBatches * batchSize; 2308 Nr = numCells % (numBatches * batchSize); 2309 offset = numCells - Nr; 2310 if (!affineQuad) PetscCall(DMFieldCreateFEGeom(coordField, cellIS, q, PETSC_FALSE, &cgeomFEM)); 2311 PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom)); 2312 PetscCall(PetscFEIntegrate(prob, f, Ne, chunkGeom, u, probAux, a, cintegral)); 2313 PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &chunkGeom)); 2314 PetscCall(PetscFEIntegrate(prob, f, Nr, chunkGeom, &u[offset * totDim], probAux, &a[offset * totDimAux], &cintegral[offset * Nf])); 2315 PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &chunkGeom)); 2316 if (!affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM)); 2317 } else if (id == PETSCFV_CLASSID) { 2318 PetscInt foff; 2319 PetscPointFunc obj_func; 2320 PetscScalar lint; 2321 2322 PetscCall(PetscDSGetObjective(prob, f, &obj_func)); 2323 PetscCall(PetscDSGetFieldOffset(prob, f, &foff)); 2324 if (obj_func) { 2325 for (c = 0; c < numCells; ++c) { 2326 PetscScalar *u_x; 2327 2328 PetscCall(DMPlexPointLocalRead(dmGrad, c, lgrad, &u_x)); 2329 obj_func(dim, Nf, NfAux, uOff, uOff_x, &u[totDim * c + foff], NULL, u_x, aOff, NULL, &a[totDimAux * c], NULL, NULL, 0.0, cgeomFVM[c].centroid, numConstants, constants, &lint); 2330 cintegral[c * Nf + f] += PetscRealPart(lint) * cgeomFVM[c].volume; 2331 } 2332 } 2333 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f); 2334 } 2335 /* Cleanup data arrays */ 2336 if (useFVM) { 2337 PetscCall(VecRestoreArrayRead(locGrad, &lgrad)); 2338 PetscCall(VecRestoreArrayRead(cellGeometryFVM, (const PetscScalar **)&cgeomFVM)); 2339 PetscCall(DMRestoreLocalVector(dmGrad, &locGrad)); 2340 PetscCall(VecDestroy(&faceGeometryFVM)); 2341 PetscCall(VecDestroy(&cellGeometryFVM)); 2342 PetscCall(DMDestroy(&dmGrad)); 2343 } 2344 if (dmAux) PetscCall(PetscFree(a)); 2345 PetscCall(PetscFree(u)); 2346 /* Cleanup */ 2347 if (affineQuad) PetscCall(PetscFEGeomDestroy(&cgeomFEM)); 2348 PetscCall(PetscQuadratureDestroy(&affineQuad)); 2349 PetscCall(ISDestroy(&cellIS)); 2350 PetscFunctionReturn(PETSC_SUCCESS); 2351 } 2352 2353 /*@ 2354 DMPlexComputeIntegralFEM - Form the integral over the domain from the global input X using pointwise functions specified by the user 2355 2356 Input Parameters: 2357 + dm - The mesh 2358 . X - Global input vector 2359 - user - The user context 2360 2361 Output Parameter: 2362 . integral - Integral for each field 2363 2364 Level: developer 2365 2366 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()` 2367 @*/ 2368 PetscErrorCode DMPlexComputeIntegralFEM(DM dm, Vec X, PetscScalar *integral, void *user) 2369 { 2370 DM_Plex *mesh = (DM_Plex *)dm->data; 2371 PetscScalar *cintegral, *lintegral; 2372 PetscInt Nf, f, cellHeight, cStart, cEnd, cell; 2373 Vec locX; 2374 2375 PetscFunctionBegin; 2376 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 2377 PetscValidHeaderSpecific(X, VEC_CLASSID, 2); 2378 PetscAssertPointer(integral, 3); 2379 PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0)); 2380 PetscCall(DMGetNumFields(dm, &Nf)); 2381 PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight)); 2382 PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd)); 2383 /* TODO Introduce a loop over large chunks (right now this is a single chunk) */ 2384 PetscCall(PetscCalloc2(Nf, &lintegral, (cEnd - cStart) * Nf, &cintegral)); 2385 /* Get local solution with boundary values */ 2386 PetscCall(DMGetLocalVector(dm, &locX)); 2387 PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL)); 2388 PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX)); 2389 PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX)); 2390 PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, user)); 2391 PetscCall(DMRestoreLocalVector(dm, &locX)); 2392 /* Sum up values */ 2393 for (cell = cStart; cell < cEnd; ++cell) { 2394 const PetscInt c = cell - cStart; 2395 2396 if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf])); 2397 for (f = 0; f < Nf; ++f) lintegral[f] += cintegral[c * Nf + f]; 2398 } 2399 PetscCall(MPIU_Allreduce(lintegral, integral, Nf, MPIU_SCALAR, MPIU_SUM, PetscObjectComm((PetscObject)dm))); 2400 if (mesh->printFEM) { 2401 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "Integral:")); 2402 for (f = 0; f < Nf; ++f) PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), " %g", (double)PetscRealPart(integral[f]))); 2403 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "\n")); 2404 } 2405 PetscCall(PetscFree2(lintegral, cintegral)); 2406 PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0)); 2407 PetscFunctionReturn(PETSC_SUCCESS); 2408 } 2409 2410 /*@ 2411 DMPlexComputeCellwiseIntegralFEM - Form the vector of cellwise integrals F from the global input X using pointwise functions specified by the user 2412 2413 Input Parameters: 2414 + dm - The mesh 2415 . X - Global input vector 2416 - user - The user context 2417 2418 Output Parameter: 2419 . F - Cellwise integrals for each field 2420 2421 Level: developer 2422 2423 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSNESComputeResidualFEM()` 2424 @*/ 2425 PetscErrorCode DMPlexComputeCellwiseIntegralFEM(DM dm, Vec X, Vec F, void *user) 2426 { 2427 DM_Plex *mesh = (DM_Plex *)dm->data; 2428 DM dmF; 2429 PetscSection sectionF; 2430 PetscScalar *cintegral, *af; 2431 PetscInt Nf, f, cellHeight, cStart, cEnd, cell; 2432 Vec locX; 2433 2434 PetscFunctionBegin; 2435 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 2436 PetscValidHeaderSpecific(X, VEC_CLASSID, 2); 2437 PetscValidHeaderSpecific(F, VEC_CLASSID, 3); 2438 PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0)); 2439 PetscCall(DMGetNumFields(dm, &Nf)); 2440 PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight)); 2441 PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd)); 2442 /* TODO Introduce a loop over large chunks (right now this is a single chunk) */ 2443 PetscCall(PetscCalloc1((cEnd - cStart) * Nf, &cintegral)); 2444 /* Get local solution with boundary values */ 2445 PetscCall(DMGetLocalVector(dm, &locX)); 2446 PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL)); 2447 PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX)); 2448 PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX)); 2449 PetscCall(DMPlexComputeIntegral_Internal(dm, locX, cStart, cEnd, cintegral, user)); 2450 PetscCall(DMRestoreLocalVector(dm, &locX)); 2451 /* Put values in F */ 2452 PetscCall(VecGetDM(F, &dmF)); 2453 PetscCall(DMGetLocalSection(dmF, §ionF)); 2454 PetscCall(VecGetArray(F, &af)); 2455 for (cell = cStart; cell < cEnd; ++cell) { 2456 const PetscInt c = cell - cStart; 2457 PetscInt dof, off; 2458 2459 if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, "Cell Integral", Nf, &cintegral[c * Nf])); 2460 PetscCall(PetscSectionGetDof(sectionF, cell, &dof)); 2461 PetscCall(PetscSectionGetOffset(sectionF, cell, &off)); 2462 PetscCheck(dof == Nf, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "The number of cell dofs %" PetscInt_FMT " != %" PetscInt_FMT, dof, Nf); 2463 for (f = 0; f < Nf; ++f) af[off + f] = cintegral[c * Nf + f]; 2464 } 2465 PetscCall(VecRestoreArray(F, &af)); 2466 PetscCall(PetscFree(cintegral)); 2467 PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0)); 2468 PetscFunctionReturn(PETSC_SUCCESS); 2469 } 2470 2471 static PetscErrorCode DMPlexComputeBdIntegral_Internal(DM dm, Vec locX, IS pointIS, void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscScalar *fintegral, void *user) 2472 { 2473 DM plex = NULL, plexA = NULL; 2474 DMEnclosureType encAux; 2475 PetscDS prob, probAux = NULL; 2476 PetscSection section, sectionAux = NULL; 2477 Vec locA = NULL; 2478 DMField coordField; 2479 PetscInt Nf, totDim, *uOff, *uOff_x; 2480 PetscInt NfAux = 0, totDimAux = 0, *aOff = NULL; 2481 PetscScalar *u, *a = NULL; 2482 const PetscScalar *constants; 2483 PetscInt numConstants, f; 2484 2485 PetscFunctionBegin; 2486 PetscCall(DMGetCoordinateField(dm, &coordField)); 2487 PetscCall(DMConvert(dm, DMPLEX, &plex)); 2488 PetscCall(DMGetDS(dm, &prob)); 2489 PetscCall(DMGetLocalSection(dm, §ion)); 2490 PetscCall(PetscSectionGetNumFields(section, &Nf)); 2491 /* Determine which discretizations we have */ 2492 for (f = 0; f < Nf; ++f) { 2493 PetscObject obj; 2494 PetscClassId id; 2495 2496 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 2497 PetscCall(PetscObjectGetClassId(obj, &id)); 2498 PetscCheck(id != PETSCFV_CLASSID, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Not supported for FVM (field %" PetscInt_FMT ")", f); 2499 } 2500 /* Read DS information */ 2501 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 2502 PetscCall(PetscDSGetComponentOffsets(prob, &uOff)); 2503 PetscCall(PetscDSGetComponentDerivativeOffsets(prob, &uOff_x)); 2504 PetscCall(PetscDSGetConstants(prob, &numConstants, &constants)); 2505 /* Read Auxiliary DS information */ 2506 PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA)); 2507 if (locA) { 2508 DM dmAux; 2509 2510 PetscCall(VecGetDM(locA, &dmAux)); 2511 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 2512 PetscCall(DMConvert(dmAux, DMPLEX, &plexA)); 2513 PetscCall(DMGetDS(dmAux, &probAux)); 2514 PetscCall(PetscDSGetNumFields(probAux, &NfAux)); 2515 PetscCall(DMGetLocalSection(dmAux, §ionAux)); 2516 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 2517 PetscCall(PetscDSGetComponentOffsets(probAux, &aOff)); 2518 } 2519 /* Integrate over points */ 2520 { 2521 PetscFEGeom *fgeom, *chunkGeom = NULL; 2522 PetscInt maxDegree; 2523 PetscQuadrature qGeom = NULL; 2524 const PetscInt *points; 2525 PetscInt numFaces, face, Nq, field; 2526 PetscInt numChunks, chunkSize, chunk, Nr, offset; 2527 2528 PetscCall(ISGetLocalSize(pointIS, &numFaces)); 2529 PetscCall(ISGetIndices(pointIS, &points)); 2530 PetscCall(PetscCalloc2(numFaces * totDim, &u, locA ? numFaces * totDimAux : 0, &a)); 2531 PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree)); 2532 for (field = 0; field < Nf; ++field) { 2533 PetscFE fe; 2534 2535 PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe)); 2536 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom)); 2537 if (!qGeom) { 2538 PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom)); 2539 PetscCall(PetscObjectReference((PetscObject)qGeom)); 2540 } 2541 PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL)); 2542 PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom)); 2543 for (face = 0; face < numFaces; ++face) { 2544 const PetscInt point = points[face], *support; 2545 PetscScalar *x = NULL; 2546 PetscInt i; 2547 2548 PetscCall(DMPlexGetSupport(dm, point, &support)); 2549 PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x)); 2550 for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i]; 2551 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x)); 2552 if (locA) { 2553 PetscInt subp; 2554 PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp)); 2555 PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x)); 2556 for (i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i]; 2557 PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x)); 2558 } 2559 } 2560 /* Get blocking */ 2561 { 2562 PetscQuadrature q; 2563 PetscInt numBatches, batchSize, numBlocks, blockSize; 2564 PetscInt Nq, Nb; 2565 2566 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 2567 PetscCall(PetscFEGetQuadrature(fe, &q)); 2568 PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL)); 2569 PetscCall(PetscFEGetDimension(fe, &Nb)); 2570 blockSize = Nb * Nq; 2571 batchSize = numBlocks * blockSize; 2572 chunkSize = numBatches * batchSize; 2573 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 2574 numChunks = numFaces / chunkSize; 2575 Nr = numFaces % chunkSize; 2576 offset = numFaces - Nr; 2577 } 2578 /* Do integration for each field */ 2579 for (chunk = 0; chunk < numChunks; ++chunk) { 2580 PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom)); 2581 PetscCall(PetscFEIntegrateBd(prob, field, func, chunkSize, chunkGeom, u, probAux, a, fintegral)); 2582 PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom)); 2583 } 2584 PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom)); 2585 PetscCall(PetscFEIntegrateBd(prob, field, func, Nr, chunkGeom, &u[offset * totDim], probAux, a ? &a[offset * totDimAux] : NULL, &fintegral[offset * Nf])); 2586 PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom)); 2587 /* Cleanup data arrays */ 2588 PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom)); 2589 PetscCall(PetscQuadratureDestroy(&qGeom)); 2590 PetscCall(PetscFree2(u, a)); 2591 PetscCall(ISRestoreIndices(pointIS, &points)); 2592 } 2593 } 2594 if (plex) PetscCall(DMDestroy(&plex)); 2595 if (plexA) PetscCall(DMDestroy(&plexA)); 2596 PetscFunctionReturn(PETSC_SUCCESS); 2597 } 2598 2599 /*@C 2600 DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user 2601 2602 Input Parameters: 2603 + dm - The mesh 2604 . X - Global input vector 2605 . label - The boundary `DMLabel` 2606 . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values 2607 . vals - The label values to use, or NULL for all values 2608 . func - The function to integrate along the boundary 2609 - user - The user context 2610 2611 Output Parameter: 2612 . integral - Integral for each field 2613 2614 Level: developer 2615 2616 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()` 2617 @*/ 2618 PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[], void (*func)(PetscInt, PetscInt, PetscInt, const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], const PetscInt[], const PetscInt[], const PetscScalar[], const PetscScalar[], const PetscScalar[], PetscReal, const PetscReal[], const PetscReal[], PetscInt, const PetscScalar[], PetscScalar[]), PetscScalar *integral, void *user) 2619 { 2620 Vec locX; 2621 PetscSection section; 2622 DMLabel depthLabel; 2623 IS facetIS; 2624 PetscInt dim, Nf, f, v; 2625 2626 PetscFunctionBegin; 2627 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 2628 PetscValidHeaderSpecific(X, VEC_CLASSID, 2); 2629 PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 3); 2630 if (vals) PetscAssertPointer(vals, 5); 2631 PetscAssertPointer(integral, 7); 2632 PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0)); 2633 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 2634 PetscCall(DMGetDimension(dm, &dim)); 2635 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 2636 PetscCall(DMGetLocalSection(dm, §ion)); 2637 PetscCall(PetscSectionGetNumFields(section, &Nf)); 2638 /* Get local solution with boundary values */ 2639 PetscCall(DMGetLocalVector(dm, &locX)); 2640 PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL)); 2641 PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX)); 2642 PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX)); 2643 /* Loop over label values */ 2644 PetscCall(PetscArrayzero(integral, Nf)); 2645 for (v = 0; v < numVals; ++v) { 2646 IS pointIS; 2647 PetscInt numFaces, face; 2648 PetscScalar *fintegral; 2649 2650 PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS)); 2651 if (!pointIS) continue; /* No points with that id on this process */ 2652 { 2653 IS isectIS; 2654 2655 /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */ 2656 PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS)); 2657 PetscCall(ISDestroy(&pointIS)); 2658 pointIS = isectIS; 2659 } 2660 PetscCall(ISGetLocalSize(pointIS, &numFaces)); 2661 PetscCall(PetscCalloc1(numFaces * Nf, &fintegral)); 2662 PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, func, fintegral, user)); 2663 /* Sum point contributions into integral */ 2664 for (f = 0; f < Nf; ++f) 2665 for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f]; 2666 PetscCall(PetscFree(fintegral)); 2667 PetscCall(ISDestroy(&pointIS)); 2668 } 2669 PetscCall(DMRestoreLocalVector(dm, &locX)); 2670 PetscCall(ISDestroy(&facetIS)); 2671 PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0)); 2672 PetscFunctionReturn(PETSC_SUCCESS); 2673 } 2674 2675 /*@ 2676 DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix I from the coarse `DM` to a uniformly refined `DM`. 2677 2678 Input Parameters: 2679 + dmc - The coarse mesh 2680 . dmf - The fine mesh 2681 . isRefined - Flag indicating regular refinement, rather than the same topology 2682 - user - The user context 2683 2684 Output Parameter: 2685 . In - The interpolation matrix 2686 2687 Level: developer 2688 2689 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()` 2690 @*/ 2691 PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user) 2692 { 2693 DM_Plex *mesh = (DM_Plex *)dmc->data; 2694 const char *name = "Interpolator"; 2695 PetscFE *feRef; 2696 PetscFV *fvRef; 2697 PetscSection fsection, fglobalSection; 2698 PetscSection csection, cglobalSection; 2699 PetscScalar *elemMat; 2700 PetscInt dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c; 2701 PetscInt cTotDim = 0, rTotDim = 0; 2702 2703 PetscFunctionBegin; 2704 PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0)); 2705 PetscCall(DMGetDimension(dmf, &dim)); 2706 PetscCall(DMGetLocalSection(dmf, &fsection)); 2707 PetscCall(DMGetGlobalSection(dmf, &fglobalSection)); 2708 PetscCall(DMGetLocalSection(dmc, &csection)); 2709 PetscCall(DMGetGlobalSection(dmc, &cglobalSection)); 2710 PetscCall(PetscSectionGetNumFields(fsection, &Nf)); 2711 PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd)); 2712 PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef)); 2713 for (f = 0; f < Nf; ++f) { 2714 PetscObject obj, objc; 2715 PetscClassId id, idc; 2716 PetscInt rNb = 0, Nc = 0, cNb = 0; 2717 2718 PetscCall(DMGetField(dmf, f, NULL, &obj)); 2719 PetscCall(PetscObjectGetClassId(obj, &id)); 2720 if (id == PETSCFE_CLASSID) { 2721 PetscFE fe = (PetscFE)obj; 2722 2723 if (isRefined) { 2724 PetscCall(PetscFERefine(fe, &feRef[f])); 2725 } else { 2726 PetscCall(PetscObjectReference((PetscObject)fe)); 2727 feRef[f] = fe; 2728 } 2729 PetscCall(PetscFEGetDimension(feRef[f], &rNb)); 2730 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 2731 } else if (id == PETSCFV_CLASSID) { 2732 PetscFV fv = (PetscFV)obj; 2733 PetscDualSpace Q; 2734 2735 if (isRefined) { 2736 PetscCall(PetscFVRefine(fv, &fvRef[f])); 2737 } else { 2738 PetscCall(PetscObjectReference((PetscObject)fv)); 2739 fvRef[f] = fv; 2740 } 2741 PetscCall(PetscFVGetDualSpace(fvRef[f], &Q)); 2742 PetscCall(PetscDualSpaceGetDimension(Q, &rNb)); 2743 PetscCall(PetscFVGetDualSpace(fv, &Q)); 2744 PetscCall(PetscFVGetNumComponents(fv, &Nc)); 2745 } 2746 PetscCall(DMGetField(dmc, f, NULL, &objc)); 2747 PetscCall(PetscObjectGetClassId(objc, &idc)); 2748 if (idc == PETSCFE_CLASSID) { 2749 PetscFE fe = (PetscFE)objc; 2750 2751 PetscCall(PetscFEGetDimension(fe, &cNb)); 2752 } else if (id == PETSCFV_CLASSID) { 2753 PetscFV fv = (PetscFV)obj; 2754 PetscDualSpace Q; 2755 2756 PetscCall(PetscFVGetDualSpace(fv, &Q)); 2757 PetscCall(PetscDualSpaceGetDimension(Q, &cNb)); 2758 } 2759 rTotDim += rNb; 2760 cTotDim += cNb; 2761 } 2762 PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat)); 2763 PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim)); 2764 for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) { 2765 PetscDualSpace Qref; 2766 PetscQuadrature f; 2767 const PetscReal *qpoints, *qweights; 2768 PetscReal *points; 2769 PetscInt npoints = 0, Nc, Np, fpdim, i, k, p, d; 2770 2771 /* Compose points from all dual basis functionals */ 2772 if (feRef[fieldI]) { 2773 PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref)); 2774 PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc)); 2775 } else { 2776 PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref)); 2777 PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc)); 2778 } 2779 PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim)); 2780 for (i = 0; i < fpdim; ++i) { 2781 PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f)); 2782 PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL)); 2783 npoints += Np; 2784 } 2785 PetscCall(PetscMalloc1(npoints * dim, &points)); 2786 for (i = 0, k = 0; i < fpdim; ++i) { 2787 PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f)); 2788 PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL)); 2789 for (p = 0; p < Np; ++p, ++k) 2790 for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d]; 2791 } 2792 2793 for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) { 2794 PetscObject obj; 2795 PetscClassId id; 2796 PetscInt NcJ = 0, cpdim = 0, j, qNc; 2797 2798 PetscCall(DMGetField(dmc, fieldJ, NULL, &obj)); 2799 PetscCall(PetscObjectGetClassId(obj, &id)); 2800 if (id == PETSCFE_CLASSID) { 2801 PetscFE fe = (PetscFE)obj; 2802 PetscTabulation T = NULL; 2803 2804 /* Evaluate basis at points */ 2805 PetscCall(PetscFEGetNumComponents(fe, &NcJ)); 2806 PetscCall(PetscFEGetDimension(fe, &cpdim)); 2807 /* For now, fields only interpolate themselves */ 2808 if (fieldI == fieldJ) { 2809 PetscCheck(Nc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, Nc, NcJ); 2810 PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T)); 2811 for (i = 0, k = 0; i < fpdim; ++i) { 2812 PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f)); 2813 PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights)); 2814 PetscCheck(qNc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, NcJ); 2815 for (p = 0; p < Np; ++p, ++k) { 2816 for (j = 0; j < cpdim; ++j) { 2817 /* 2818 cTotDim: Total columns in element interpolation matrix, sum of number of dual basis functionals in each field 2819 offsetI, offsetJ: Offsets into the larger element interpolation matrix for different fields 2820 fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals 2821 qNC, Nc, Ncj, c: Number of components in this field 2822 Np, p: Number of quad points in the fine grid functional i 2823 k: i*Np + p, overall point number for the interpolation 2824 */ 2825 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += T->T[0][k * cpdim * NcJ + j * Nc + c] * qweights[p * qNc + c]; 2826 } 2827 } 2828 } 2829 PetscCall(PetscTabulationDestroy(&T)); 2830 } 2831 } else if (id == PETSCFV_CLASSID) { 2832 PetscFV fv = (PetscFV)obj; 2833 2834 /* Evaluate constant function at points */ 2835 PetscCall(PetscFVGetNumComponents(fv, &NcJ)); 2836 cpdim = 1; 2837 /* For now, fields only interpolate themselves */ 2838 if (fieldI == fieldJ) { 2839 PetscCheck(Nc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, Nc, NcJ); 2840 for (i = 0, k = 0; i < fpdim; ++i) { 2841 PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f)); 2842 PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights)); 2843 PetscCheck(qNc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, NcJ); 2844 for (p = 0; p < Np; ++p, ++k) { 2845 for (j = 0; j < cpdim; ++j) { 2846 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c]; 2847 } 2848 } 2849 } 2850 } 2851 } 2852 offsetJ += cpdim; 2853 } 2854 offsetI += fpdim; 2855 PetscCall(PetscFree(points)); 2856 } 2857 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat)); 2858 /* Preallocate matrix */ 2859 { 2860 Mat preallocator; 2861 PetscScalar *vals; 2862 PetscInt *cellCIndices, *cellFIndices; 2863 PetscInt locRows, locCols, cell; 2864 2865 PetscCall(MatGetLocalSize(In, &locRows, &locCols)); 2866 PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator)); 2867 PetscCall(MatSetType(preallocator, MATPREALLOCATOR)); 2868 PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE)); 2869 PetscCall(MatSetUp(preallocator)); 2870 PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices)); 2871 for (cell = cStart; cell < cEnd; ++cell) { 2872 if (isRefined) { 2873 PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices)); 2874 PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES)); 2875 } else { 2876 PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, preallocator, cell, vals, INSERT_VALUES)); 2877 } 2878 } 2879 PetscCall(PetscFree3(vals, cellCIndices, cellFIndices)); 2880 PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY)); 2881 PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY)); 2882 PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In)); 2883 PetscCall(MatDestroy(&preallocator)); 2884 } 2885 /* Fill matrix */ 2886 PetscCall(MatZeroEntries(In)); 2887 for (c = cStart; c < cEnd; ++c) { 2888 if (isRefined) { 2889 PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES)); 2890 } else { 2891 PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, In, c, elemMat, INSERT_VALUES)); 2892 } 2893 } 2894 for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f])); 2895 PetscCall(PetscFree2(feRef, fvRef)); 2896 PetscCall(PetscFree(elemMat)); 2897 PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY)); 2898 PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY)); 2899 if (mesh->printFEM > 1) { 2900 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name)); 2901 PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE)); 2902 PetscCall(MatView(In, NULL)); 2903 } 2904 PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0)); 2905 PetscFunctionReturn(PETSC_SUCCESS); 2906 } 2907 2908 PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user) 2909 { 2910 SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness"); 2911 } 2912 2913 /*@ 2914 DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix I from the coarse `DM` to a non-nested fine `DM`. 2915 2916 Input Parameters: 2917 + dmf - The fine mesh 2918 . dmc - The coarse mesh 2919 - user - The user context 2920 2921 Output Parameter: 2922 . In - The interpolation matrix 2923 2924 Level: developer 2925 2926 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()` 2927 @*/ 2928 PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user) 2929 { 2930 DM_Plex *mesh = (DM_Plex *)dmf->data; 2931 const char *name = "Interpolator"; 2932 PetscDS prob; 2933 Mat interp; 2934 PetscSection fsection, globalFSection; 2935 PetscSection csection, globalCSection; 2936 PetscInt locRows, locCols; 2937 PetscReal *x, *v0, *J, *invJ, detJ; 2938 PetscReal *v0c, *Jc, *invJc, detJc; 2939 PetscScalar *elemMat; 2940 PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s; 2941 2942 PetscFunctionBegin; 2943 PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0)); 2944 PetscCall(DMGetCoordinateDim(dmc, &dim)); 2945 PetscCall(DMGetDS(dmc, &prob)); 2946 PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL)); 2947 PetscCall(PetscDSGetNumFields(prob, &Nf)); 2948 PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ)); 2949 PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc)); 2950 PetscCall(DMGetLocalSection(dmf, &fsection)); 2951 PetscCall(DMGetGlobalSection(dmf, &globalFSection)); 2952 PetscCall(DMGetLocalSection(dmc, &csection)); 2953 PetscCall(DMGetGlobalSection(dmc, &globalCSection)); 2954 PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd)); 2955 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 2956 PetscCall(PetscMalloc1(totDim, &elemMat)); 2957 2958 PetscCall(MatGetLocalSize(In, &locRows, &locCols)); 2959 PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp)); 2960 PetscCall(MatSetType(interp, MATPREALLOCATOR)); 2961 PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE)); 2962 PetscCall(MatSetUp(interp)); 2963 for (s = 0; s < 2; ++s) { 2964 for (field = 0; field < Nf; ++field) { 2965 PetscObject obj; 2966 PetscClassId id; 2967 PetscDualSpace Q = NULL; 2968 PetscTabulation T = NULL; 2969 PetscQuadrature f; 2970 const PetscReal *qpoints, *qweights; 2971 PetscInt Nc, qNc, Np, fpdim, off, i, d; 2972 2973 PetscCall(PetscDSGetFieldOffset(prob, field, &off)); 2974 PetscCall(PetscDSGetDiscretization(prob, field, &obj)); 2975 PetscCall(PetscObjectGetClassId(obj, &id)); 2976 if (id == PETSCFE_CLASSID) { 2977 PetscFE fe = (PetscFE)obj; 2978 2979 PetscCall(PetscFEGetDualSpace(fe, &Q)); 2980 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 2981 if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T)); 2982 } else if (id == PETSCFV_CLASSID) { 2983 PetscFV fv = (PetscFV)obj; 2984 2985 PetscCall(PetscFVGetDualSpace(fv, &Q)); 2986 Nc = 1; 2987 } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 2988 PetscCall(PetscDualSpaceGetDimension(Q, &fpdim)); 2989 /* For each fine grid cell */ 2990 for (cell = cStart; cell < cEnd; ++cell) { 2991 PetscInt *findices, *cindices; 2992 PetscInt numFIndices, numCIndices; 2993 2994 PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 2995 PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ)); 2996 PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim); 2997 for (i = 0; i < fpdim; ++i) { 2998 Vec pointVec; 2999 PetscScalar *pV; 3000 PetscSF coarseCellSF = NULL; 3001 const PetscSFNode *coarseCells; 3002 PetscInt numCoarseCells, cpdim, row = findices[i + off], q, c, j; 3003 3004 /* Get points from the dual basis functional quadrature */ 3005 PetscCall(PetscDualSpaceGetFunctional(Q, i, &f)); 3006 PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights)); 3007 PetscCheck(qNc == Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, Nc); 3008 PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec)); 3009 PetscCall(VecSetBlockSize(pointVec, dim)); 3010 PetscCall(VecGetArray(pointVec, &pV)); 3011 for (q = 0; q < Np; ++q) { 3012 const PetscReal xi0[3] = {-1., -1., -1.}; 3013 3014 /* Transform point to real space */ 3015 CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x); 3016 for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d]; 3017 } 3018 PetscCall(VecRestoreArray(pointVec, &pV)); 3019 /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */ 3020 /* OPT: Read this out from preallocation information */ 3021 PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF)); 3022 /* Update preallocation info */ 3023 PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells)); 3024 PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located"); 3025 PetscCall(VecGetArray(pointVec, &pV)); 3026 for (ccell = 0; ccell < numCoarseCells; ++ccell) { 3027 PetscReal pVReal[3]; 3028 const PetscReal xi0[3] = {-1., -1., -1.}; 3029 3030 PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3031 if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetDimension((PetscFE)obj, &cpdim)); 3032 else cpdim = 1; 3033 3034 if (s) { 3035 /* Transform points from real space to coarse reference space */ 3036 PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc)); 3037 for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]); 3038 CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x); 3039 3040 if (id == PETSCFE_CLASSID) { 3041 /* Evaluate coarse basis on contained point */ 3042 PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T)); 3043 PetscCall(PetscArrayzero(elemMat, cpdim)); 3044 /* Get elemMat entries by multiplying by weight */ 3045 for (j = 0; j < cpdim; ++j) { 3046 for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c]; 3047 } 3048 } else { 3049 for (j = 0; j < cpdim; ++j) { 3050 for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c]; 3051 } 3052 } 3053 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat)); 3054 } 3055 /* Update interpolator */ 3056 PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim); 3057 PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES)); 3058 PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3059 } 3060 PetscCall(VecRestoreArray(pointVec, &pV)); 3061 PetscCall(PetscSFDestroy(&coarseCellSF)); 3062 PetscCall(VecDestroy(&pointVec)); 3063 } 3064 PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3065 } 3066 if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T)); 3067 } 3068 if (!s) { 3069 PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY)); 3070 PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY)); 3071 PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In)); 3072 PetscCall(MatDestroy(&interp)); 3073 interp = In; 3074 } 3075 } 3076 PetscCall(PetscFree3(v0, J, invJ)); 3077 PetscCall(PetscFree3(v0c, Jc, invJc)); 3078 PetscCall(PetscFree(elemMat)); 3079 PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY)); 3080 PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY)); 3081 PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0)); 3082 PetscFunctionReturn(PETSC_SUCCESS); 3083 } 3084 3085 /*@ 3086 DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix M from the coarse `DM` to a non-nested fine `DM`. 3087 3088 Input Parameters: 3089 + dmf - The fine mesh 3090 . dmc - The coarse mesh 3091 - user - The user context 3092 3093 Output Parameter: 3094 . mass - The mass matrix 3095 3096 Level: developer 3097 3098 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()` 3099 @*/ 3100 PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user) 3101 { 3102 DM_Plex *mesh = (DM_Plex *)dmf->data; 3103 const char *name = "Mass Matrix"; 3104 PetscDS prob; 3105 PetscSection fsection, csection, globalFSection, globalCSection; 3106 PetscHSetIJ ht; 3107 PetscLayout rLayout; 3108 PetscInt *dnz, *onz; 3109 PetscInt locRows, rStart, rEnd; 3110 PetscReal *x, *v0, *J, *invJ, detJ; 3111 PetscReal *v0c, *Jc, *invJc, detJc; 3112 PetscScalar *elemMat; 3113 PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell; 3114 3115 PetscFunctionBegin; 3116 PetscCall(DMGetCoordinateDim(dmc, &dim)); 3117 PetscCall(DMGetDS(dmc, &prob)); 3118 PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL)); 3119 PetscCall(PetscDSGetNumFields(prob, &Nf)); 3120 PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ)); 3121 PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc)); 3122 PetscCall(DMGetLocalSection(dmf, &fsection)); 3123 PetscCall(DMGetGlobalSection(dmf, &globalFSection)); 3124 PetscCall(DMGetLocalSection(dmc, &csection)); 3125 PetscCall(DMGetGlobalSection(dmc, &globalCSection)); 3126 PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd)); 3127 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 3128 PetscCall(PetscMalloc1(totDim, &elemMat)); 3129 3130 PetscCall(MatGetLocalSize(mass, &locRows, NULL)); 3131 PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout)); 3132 PetscCall(PetscLayoutSetLocalSize(rLayout, locRows)); 3133 PetscCall(PetscLayoutSetBlockSize(rLayout, 1)); 3134 PetscCall(PetscLayoutSetUp(rLayout)); 3135 PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd)); 3136 PetscCall(PetscLayoutDestroy(&rLayout)); 3137 PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz)); 3138 PetscCall(PetscHSetIJCreate(&ht)); 3139 for (field = 0; field < Nf; ++field) { 3140 PetscObject obj; 3141 PetscClassId id; 3142 PetscQuadrature quad; 3143 const PetscReal *qpoints; 3144 PetscInt Nq, Nc, i, d; 3145 3146 PetscCall(PetscDSGetDiscretization(prob, field, &obj)); 3147 PetscCall(PetscObjectGetClassId(obj, &id)); 3148 if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad)); 3149 else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad)); 3150 PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL)); 3151 /* For each fine grid cell */ 3152 for (cell = cStart; cell < cEnd; ++cell) { 3153 Vec pointVec; 3154 PetscScalar *pV; 3155 PetscSF coarseCellSF = NULL; 3156 const PetscSFNode *coarseCells; 3157 PetscInt numCoarseCells, q, c; 3158 PetscInt *findices, *cindices; 3159 PetscInt numFIndices, numCIndices; 3160 3161 PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3162 PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ)); 3163 /* Get points from the quadrature */ 3164 PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec)); 3165 PetscCall(VecSetBlockSize(pointVec, dim)); 3166 PetscCall(VecGetArray(pointVec, &pV)); 3167 for (q = 0; q < Nq; ++q) { 3168 const PetscReal xi0[3] = {-1., -1., -1.}; 3169 3170 /* Transform point to real space */ 3171 CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x); 3172 for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d]; 3173 } 3174 PetscCall(VecRestoreArray(pointVec, &pV)); 3175 /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */ 3176 PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF)); 3177 PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view")); 3178 /* Update preallocation info */ 3179 PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells)); 3180 PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located"); 3181 { 3182 PetscHashIJKey key; 3183 PetscBool missing; 3184 3185 for (i = 0; i < numFIndices; ++i) { 3186 key.i = findices[i]; 3187 if (key.i >= 0) { 3188 /* Get indices for coarse elements */ 3189 for (ccell = 0; ccell < numCoarseCells; ++ccell) { 3190 PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3191 for (c = 0; c < numCIndices; ++c) { 3192 key.j = cindices[c]; 3193 if (key.j < 0) continue; 3194 PetscCall(PetscHSetIJQueryAdd(ht, key, &missing)); 3195 if (missing) { 3196 if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart]; 3197 else ++onz[key.i - rStart]; 3198 } 3199 } 3200 PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3201 } 3202 } 3203 } 3204 } 3205 PetscCall(PetscSFDestroy(&coarseCellSF)); 3206 PetscCall(VecDestroy(&pointVec)); 3207 PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3208 } 3209 } 3210 PetscCall(PetscHSetIJDestroy(&ht)); 3211 PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL)); 3212 PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE)); 3213 PetscCall(PetscFree2(dnz, onz)); 3214 for (field = 0; field < Nf; ++field) { 3215 PetscObject obj; 3216 PetscClassId id; 3217 PetscTabulation T, Tfine; 3218 PetscQuadrature quad; 3219 const PetscReal *qpoints, *qweights; 3220 PetscInt Nq, Nc, i, d; 3221 3222 PetscCall(PetscDSGetDiscretization(prob, field, &obj)); 3223 PetscCall(PetscObjectGetClassId(obj, &id)); 3224 if (id == PETSCFE_CLASSID) { 3225 PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad)); 3226 PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine)); 3227 PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T)); 3228 } else { 3229 PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad)); 3230 } 3231 PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights)); 3232 /* For each fine grid cell */ 3233 for (cell = cStart; cell < cEnd; ++cell) { 3234 Vec pointVec; 3235 PetscScalar *pV; 3236 PetscSF coarseCellSF = NULL; 3237 const PetscSFNode *coarseCells; 3238 PetscInt numCoarseCells, cpdim, q, c, j; 3239 PetscInt *findices, *cindices; 3240 PetscInt numFIndices, numCIndices; 3241 3242 PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3243 PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ)); 3244 /* Get points from the quadrature */ 3245 PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec)); 3246 PetscCall(VecSetBlockSize(pointVec, dim)); 3247 PetscCall(VecGetArray(pointVec, &pV)); 3248 for (q = 0; q < Nq; ++q) { 3249 const PetscReal xi0[3] = {-1., -1., -1.}; 3250 3251 /* Transform point to real space */ 3252 CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x); 3253 for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d]; 3254 } 3255 PetscCall(VecRestoreArray(pointVec, &pV)); 3256 /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */ 3257 PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF)); 3258 /* Update matrix */ 3259 PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells)); 3260 PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located"); 3261 PetscCall(VecGetArray(pointVec, &pV)); 3262 for (ccell = 0; ccell < numCoarseCells; ++ccell) { 3263 PetscReal pVReal[3]; 3264 const PetscReal xi0[3] = {-1., -1., -1.}; 3265 3266 PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3267 /* Transform points from real space to coarse reference space */ 3268 PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc)); 3269 for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]); 3270 CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x); 3271 3272 if (id == PETSCFE_CLASSID) { 3273 PetscFE fe = (PetscFE)obj; 3274 3275 /* Evaluate coarse basis on contained point */ 3276 PetscCall(PetscFEGetDimension(fe, &cpdim)); 3277 PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T)); 3278 /* Get elemMat entries by multiplying by weight */ 3279 for (i = 0; i < numFIndices; ++i) { 3280 PetscCall(PetscArrayzero(elemMat, cpdim)); 3281 for (j = 0; j < cpdim; ++j) { 3282 for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * Tfine->T[0][(ccell * numFIndices + i) * Nc + c] * qweights[ccell * Nc + c] * detJ; 3283 } 3284 /* Update interpolator */ 3285 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat)); 3286 PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim); 3287 PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES)); 3288 } 3289 } else { 3290 cpdim = 1; 3291 for (i = 0; i < numFIndices; ++i) { 3292 PetscCall(PetscArrayzero(elemMat, cpdim)); 3293 for (j = 0; j < cpdim; ++j) { 3294 for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ; 3295 } 3296 /* Update interpolator */ 3297 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat)); 3298 PetscCall(PetscPrintf(PETSC_COMM_SELF, "Nq: %" PetscInt_FMT " %" PetscInt_FMT " Nf: %" PetscInt_FMT " %" PetscInt_FMT " Nc: %" PetscInt_FMT " %" PetscInt_FMT "\n", ccell, Nq, i, numFIndices, j, numCIndices)); 3299 PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim); 3300 PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES)); 3301 } 3302 } 3303 PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3304 } 3305 PetscCall(VecRestoreArray(pointVec, &pV)); 3306 PetscCall(PetscSFDestroy(&coarseCellSF)); 3307 PetscCall(VecDestroy(&pointVec)); 3308 PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3309 } 3310 if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T)); 3311 } 3312 PetscCall(PetscFree3(v0, J, invJ)); 3313 PetscCall(PetscFree3(v0c, Jc, invJc)); 3314 PetscCall(PetscFree(elemMat)); 3315 PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY)); 3316 PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY)); 3317 PetscFunctionReturn(PETSC_SUCCESS); 3318 } 3319 3320 /*@ 3321 DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns 3322 3323 Input Parameters: 3324 + dmc - The coarse mesh 3325 . dmf - The fine mesh 3326 - user - The user context 3327 3328 Output Parameter: 3329 . sc - The mapping 3330 3331 Level: developer 3332 3333 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()` 3334 @*/ 3335 PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user) 3336 { 3337 PetscDS prob; 3338 PetscFE *feRef; 3339 PetscFV *fvRef; 3340 Vec fv, cv; 3341 IS fis, cis; 3342 PetscSection fsection, fglobalSection, csection, cglobalSection; 3343 PetscInt *cmap, *cellCIndices, *cellFIndices, *cindices, *findices; 3344 PetscInt cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m; 3345 PetscBool *needAvg; 3346 3347 PetscFunctionBegin; 3348 PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0)); 3349 PetscCall(DMGetDimension(dmf, &dim)); 3350 PetscCall(DMGetLocalSection(dmf, &fsection)); 3351 PetscCall(DMGetGlobalSection(dmf, &fglobalSection)); 3352 PetscCall(DMGetLocalSection(dmc, &csection)); 3353 PetscCall(DMGetGlobalSection(dmc, &cglobalSection)); 3354 PetscCall(PetscSectionGetNumFields(fsection, &Nf)); 3355 PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd)); 3356 PetscCall(DMGetDS(dmc, &prob)); 3357 PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg)); 3358 for (f = 0; f < Nf; ++f) { 3359 PetscObject obj; 3360 PetscClassId id; 3361 PetscInt fNb = 0, Nc = 0; 3362 3363 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 3364 PetscCall(PetscObjectGetClassId(obj, &id)); 3365 if (id == PETSCFE_CLASSID) { 3366 PetscFE fe = (PetscFE)obj; 3367 PetscSpace sp; 3368 PetscInt maxDegree; 3369 3370 PetscCall(PetscFERefine(fe, &feRef[f])); 3371 PetscCall(PetscFEGetDimension(feRef[f], &fNb)); 3372 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 3373 PetscCall(PetscFEGetBasisSpace(fe, &sp)); 3374 PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree)); 3375 if (!maxDegree) needAvg[f] = PETSC_TRUE; 3376 } else if (id == PETSCFV_CLASSID) { 3377 PetscFV fv = (PetscFV)obj; 3378 PetscDualSpace Q; 3379 3380 PetscCall(PetscFVRefine(fv, &fvRef[f])); 3381 PetscCall(PetscFVGetDualSpace(fvRef[f], &Q)); 3382 PetscCall(PetscDualSpaceGetDimension(Q, &fNb)); 3383 PetscCall(PetscFVGetNumComponents(fv, &Nc)); 3384 needAvg[f] = PETSC_TRUE; 3385 } 3386 fTotDim += fNb; 3387 } 3388 PetscCall(PetscDSGetTotalDimension(prob, &cTotDim)); 3389 PetscCall(PetscMalloc1(cTotDim, &cmap)); 3390 for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) { 3391 PetscFE feC; 3392 PetscFV fvC; 3393 PetscDualSpace QF, QC; 3394 PetscInt order = -1, NcF, NcC, fpdim, cpdim; 3395 3396 if (feRef[field]) { 3397 PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC)); 3398 PetscCall(PetscFEGetNumComponents(feC, &NcC)); 3399 PetscCall(PetscFEGetNumComponents(feRef[field], &NcF)); 3400 PetscCall(PetscFEGetDualSpace(feRef[field], &QF)); 3401 PetscCall(PetscDualSpaceGetOrder(QF, &order)); 3402 PetscCall(PetscDualSpaceGetDimension(QF, &fpdim)); 3403 PetscCall(PetscFEGetDualSpace(feC, &QC)); 3404 PetscCall(PetscDualSpaceGetDimension(QC, &cpdim)); 3405 } else { 3406 PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC)); 3407 PetscCall(PetscFVGetNumComponents(fvC, &NcC)); 3408 PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF)); 3409 PetscCall(PetscFVGetDualSpace(fvRef[field], &QF)); 3410 PetscCall(PetscDualSpaceGetDimension(QF, &fpdim)); 3411 PetscCall(PetscFVGetDualSpace(fvC, &QC)); 3412 PetscCall(PetscDualSpaceGetDimension(QC, &cpdim)); 3413 } 3414 PetscCheck(NcF == NcC, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, NcF, NcC); 3415 for (c = 0; c < cpdim; ++c) { 3416 PetscQuadrature cfunc; 3417 const PetscReal *cqpoints, *cqweights; 3418 PetscInt NqcC, NpC; 3419 PetscBool found = PETSC_FALSE; 3420 3421 PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc)); 3422 PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights)); 3423 PetscCheck(NqcC == NcC, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %" PetscInt_FMT " must match number of field components %" PetscInt_FMT, NqcC, NcC); 3424 PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments"); 3425 for (f = 0; f < fpdim; ++f) { 3426 PetscQuadrature ffunc; 3427 const PetscReal *fqpoints, *fqweights; 3428 PetscReal sum = 0.0; 3429 PetscInt NqcF, NpF; 3430 3431 PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc)); 3432 PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights)); 3433 PetscCheck(NqcF == NcF, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %" PetscInt_FMT " must match number of field components %" PetscInt_FMT, NqcF, NcF); 3434 if (NpC != NpF) continue; 3435 for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]); 3436 if (sum > 1.0e-9) continue; 3437 for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]); 3438 if (sum < 1.0e-9) continue; 3439 cmap[offsetC + c] = offsetF + f; 3440 found = PETSC_TRUE; 3441 break; 3442 } 3443 if (!found) { 3444 /* TODO We really want the average here, but some asshole put VecScatter in the interface */ 3445 if (fvRef[field] || (feRef[field] && order == 0)) { 3446 cmap[offsetC + c] = offsetF + 0; 3447 } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection"); 3448 } 3449 } 3450 offsetC += cpdim; 3451 offsetF += fpdim; 3452 } 3453 for (f = 0; f < Nf; ++f) { 3454 PetscCall(PetscFEDestroy(&feRef[f])); 3455 PetscCall(PetscFVDestroy(&fvRef[f])); 3456 } 3457 PetscCall(PetscFree3(feRef, fvRef, needAvg)); 3458 3459 PetscCall(DMGetGlobalVector(dmf, &fv)); 3460 PetscCall(DMGetGlobalVector(dmc, &cv)); 3461 PetscCall(VecGetOwnershipRange(cv, &startC, &endC)); 3462 PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m)); 3463 PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices)); 3464 PetscCall(PetscMalloc1(m, &cindices)); 3465 PetscCall(PetscMalloc1(m, &findices)); 3466 for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1; 3467 for (c = cStart; c < cEnd; ++c) { 3468 PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices)); 3469 for (d = 0; d < cTotDim; ++d) { 3470 if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue; 3471 PetscCheck(!(findices[cellCIndices[d] - startC] >= 0) || !(findices[cellCIndices[d] - startC] != cellFIndices[cmap[d]]), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Cell %" PetscInt_FMT " Coarse dof %" PetscInt_FMT " maps to both %" PetscInt_FMT " and %" PetscInt_FMT, c, cindices[cellCIndices[d] - startC], findices[cellCIndices[d] - startC], cellFIndices[cmap[d]]); 3472 cindices[cellCIndices[d] - startC] = cellCIndices[d]; 3473 findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]]; 3474 } 3475 } 3476 PetscCall(PetscFree(cmap)); 3477 PetscCall(PetscFree2(cellCIndices, cellFIndices)); 3478 3479 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis)); 3480 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis)); 3481 PetscCall(VecScatterCreate(cv, cis, fv, fis, sc)); 3482 PetscCall(ISDestroy(&cis)); 3483 PetscCall(ISDestroy(&fis)); 3484 PetscCall(DMRestoreGlobalVector(dmf, &fv)); 3485 PetscCall(DMRestoreGlobalVector(dmc, &cv)); 3486 PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0)); 3487 PetscFunctionReturn(PETSC_SUCCESS); 3488 } 3489 3490 /*@C 3491 DMPlexGetCellFields - Retrieve the field values values for a chunk of cells 3492 3493 Input Parameters: 3494 + dm - The `DM` 3495 . cellIS - The cells to include 3496 . locX - A local vector with the solution fields 3497 . locX_t - A local vector with solution field time derivatives, or NULL 3498 - locA - A local vector with auxiliary fields, or NULL 3499 3500 Output Parameters: 3501 + u - The field coefficients 3502 . u_t - The fields derivative coefficients 3503 - a - The auxiliary field coefficients 3504 3505 Level: developer 3506 3507 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()` 3508 @*/ 3509 PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a) 3510 { 3511 DM plex, plexA = NULL; 3512 DMEnclosureType encAux; 3513 PetscSection section, sectionAux; 3514 PetscDS prob; 3515 const PetscInt *cells; 3516 PetscInt cStart, cEnd, numCells, totDim, totDimAux, c; 3517 3518 PetscFunctionBegin; 3519 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3520 PetscValidHeaderSpecific(locX, VEC_CLASSID, 3); 3521 if (locX_t) PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 4); 3522 if (locA) PetscValidHeaderSpecific(locA, VEC_CLASSID, 5); 3523 PetscAssertPointer(u, 6); 3524 PetscAssertPointer(u_t, 7); 3525 PetscAssertPointer(a, 8); 3526 PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE)); 3527 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 3528 PetscCall(DMGetLocalSection(dm, §ion)); 3529 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL)); 3530 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 3531 if (locA) { 3532 DM dmAux; 3533 PetscDS probAux; 3534 3535 PetscCall(VecGetDM(locA, &dmAux)); 3536 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 3537 PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE)); 3538 PetscCall(DMGetLocalSection(dmAux, §ionAux)); 3539 PetscCall(DMGetDS(dmAux, &probAux)); 3540 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 3541 } 3542 numCells = cEnd - cStart; 3543 PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u)); 3544 if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t)); 3545 else *u_t = NULL; 3546 if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a)); 3547 else *a = NULL; 3548 for (c = cStart; c < cEnd; ++c) { 3549 const PetscInt cell = cells ? cells[c] : c; 3550 const PetscInt cind = c - cStart; 3551 PetscScalar *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a; 3552 PetscInt i; 3553 3554 PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x)); 3555 for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i]; 3556 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x)); 3557 if (locX_t) { 3558 PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t)); 3559 for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i]; 3560 PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t)); 3561 } 3562 if (locA) { 3563 PetscInt subcell; 3564 PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell)); 3565 PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x)); 3566 for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i]; 3567 PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x)); 3568 } 3569 } 3570 PetscCall(DMDestroy(&plex)); 3571 if (locA) PetscCall(DMDestroy(&plexA)); 3572 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 3573 PetscFunctionReturn(PETSC_SUCCESS); 3574 } 3575 3576 /*@C 3577 DMPlexRestoreCellFields - Restore the field values values for a chunk of cells 3578 3579 Input Parameters: 3580 + dm - The `DM` 3581 . cellIS - The cells to include 3582 . locX - A local vector with the solution fields 3583 . locX_t - A local vector with solution field time derivatives, or NULL 3584 - locA - A local vector with auxiliary fields, or NULL 3585 3586 Output Parameters: 3587 + u - The field coefficients 3588 . u_t - The fields derivative coefficients 3589 - a - The auxiliary field coefficients 3590 3591 Level: developer 3592 3593 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()` 3594 @*/ 3595 PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a) 3596 { 3597 PetscFunctionBegin; 3598 PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u)); 3599 if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t)); 3600 if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a)); 3601 PetscFunctionReturn(PETSC_SUCCESS); 3602 } 3603 3604 static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a) 3605 { 3606 DM plex, plexA = NULL; 3607 DMEnclosureType encAux; 3608 PetscSection section, sectionAux; 3609 PetscDS ds, dsIn; 3610 const PetscInt *cells; 3611 PetscInt cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f; 3612 3613 PetscFunctionBegin; 3614 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3615 PetscValidHeaderSpecific(cellIS, IS_CLASSID, 2); 3616 PetscValidHeaderSpecific(locX, VEC_CLASSID, 3); 3617 if (locX_t) { PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 4); } 3618 if (locA) { PetscValidHeaderSpecific(locA, VEC_CLASSID, 5); } 3619 PetscAssertPointer(u, 6); 3620 PetscAssertPointer(u_t, 7); 3621 PetscAssertPointer(a, 8); 3622 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 3623 numCells = cEnd - cStart; 3624 PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE)); 3625 PetscCall(DMGetLocalSection(dm, §ion)); 3626 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn)); 3627 PetscCall(PetscDSGetNumFields(dsIn, &Nf)); 3628 PetscCall(PetscDSGetTotalDimension(dsIn, &totDim)); 3629 if (locA) { 3630 DM dmAux; 3631 PetscDS probAux; 3632 3633 PetscCall(VecGetDM(locA, &dmAux)); 3634 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 3635 PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE)); 3636 PetscCall(DMGetLocalSection(dmAux, §ionAux)); 3637 PetscCall(DMGetDS(dmAux, &probAux)); 3638 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 3639 } 3640 PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u)); 3641 if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t)); 3642 else { 3643 *u_t = NULL; 3644 } 3645 if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a)); 3646 else { 3647 *a = NULL; 3648 } 3649 // Loop over cohesive cells 3650 for (c = cStart; c < cEnd; ++c) { 3651 const PetscInt cell = cells ? cells[c] : c; 3652 const PetscInt cind = c - cStart; 3653 PetscScalar *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL; 3654 PetscScalar *ul = &(*u)[cind * totDim], *ul_t = u_t ? &(*u_t)[cind * totDim] : NULL; 3655 const PetscInt *cone, *ornt; 3656 PetscInt Nx = 0, Nxf, s; 3657 3658 PetscCall(DMPlexGetCone(dm, cell, &cone)); 3659 PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt)); 3660 // Put in cohesive unknowns 3661 PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf)); 3662 if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t)); 3663 for (f = 0; f < Nf; ++f) { 3664 PetscInt fdofIn, foff, foffIn; 3665 PetscBool cohesive; 3666 3667 PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive)); 3668 if (!cohesive) continue; 3669 PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn)); 3670 PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff)); 3671 PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn)); 3672 for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i]; 3673 if (locX_t) 3674 for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i]; 3675 Nx += fdofIn; 3676 } 3677 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf)); 3678 if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t)); 3679 // Loop over sides of surface 3680 for (s = 0; s < 2; ++s) { 3681 const PetscInt *support; 3682 const PetscInt face = cone[s]; 3683 PetscInt ssize, ncell, Nxc; 3684 3685 // I don't think I need the face to have 0 orientation in the hybrid cell 3686 //PetscCheck(!ornt[s], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != 0", face, cell, ornt[s]); 3687 PetscCall(DMPlexGetSupport(dm, face, &support)); 3688 PetscCall(DMPlexGetSupportSize(dm, face, &ssize)); 3689 if (support[0] == cell) ncell = support[1]; 3690 else if (support[1] == cell) ncell = support[0]; 3691 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell); 3692 // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields 3693 PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc)); 3694 if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t)); 3695 for (f = 0; f < Nf; ++f) { 3696 PetscInt fdofIn, foffIn; 3697 PetscBool cohesive; 3698 3699 PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive)); 3700 if (cohesive) continue; 3701 PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn)); 3702 PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn)); 3703 for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foffIn + i]; 3704 if (locX_t) 3705 for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foffIn + i]; 3706 Nx += fdofIn; 3707 } 3708 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc)); 3709 if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t)); 3710 } 3711 PetscCheck(Nx == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for cell %" PetscInt_FMT " does not match DS size %" PetscInt_FMT, Nx, cell, totDim); 3712 3713 if (locA) { 3714 PetscScalar *al = &(*a)[cind * totDimAux]; 3715 PetscInt subcell; 3716 3717 PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell)); 3718 PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x)); 3719 PetscCheck(Nx == totDimAux, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for subcell %" PetscInt_FMT "does not match DS size %" PetscInt_FMT, Nx, subcell, totDimAux); 3720 for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i]; 3721 PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x)); 3722 } 3723 } 3724 PetscCall(DMDestroy(&plex)); 3725 PetscCall(DMDestroy(&plexA)); 3726 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 3727 PetscFunctionReturn(PETSC_SUCCESS); 3728 } 3729 3730 /* 3731 DMPlexGetHybridFields - Get the field values for the negative side (s = 0) and positive side (s = 1) of the interface 3732 3733 Input Parameters: 3734 + dm - The full domain DM 3735 . dmX - An array of DM for the field, say an auxiliary DM, indexed by s 3736 . dsX - An array of PetscDS for the field, indexed by s 3737 . cellIS - The interface cells for which we want values 3738 . locX - An array of local vectors with the field values, indexed by s 3739 - useCell - Flag to have values come from neighboring cell rather than endcap face 3740 3741 Output Parameter: 3742 . x - An array of field values, indexed by s 3743 3744 Note: 3745 The arrays in `x` will be allocated using `DMGetWorkArray()`, and must be returned using `DMPlexRestoreHybridFields()`. 3746 3747 Level: advanced 3748 3749 .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()` 3750 */ 3751 static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[]) 3752 { 3753 DM plexX[2]; 3754 DMEnclosureType encX[2]; 3755 PetscSection sectionX[2]; 3756 const PetscInt *cells; 3757 PetscInt cStart, cEnd, numCells, c, s, totDimX[2]; 3758 3759 PetscFunctionBegin; 3760 PetscAssertPointer(locX, 5); 3761 if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS); 3762 PetscAssertPointer(dmX, 2); 3763 PetscAssertPointer(dsX, 3); 3764 PetscValidHeaderSpecific(cellIS, IS_CLASSID, 4); 3765 PetscAssertPointer(x, 7); 3766 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 3767 numCells = cEnd - cStart; 3768 for (s = 0; s < 2; ++s) { 3769 PetscValidHeaderSpecific(dmX[s], DM_CLASSID, 2); 3770 PetscValidHeaderSpecific(dsX[s], PETSCDS_CLASSID, 3); 3771 PetscValidHeaderSpecific(locX[s], VEC_CLASSID, 5); 3772 PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE)); 3773 PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s])); 3774 PetscCall(DMGetLocalSection(dmX[s], §ionX[s])); 3775 PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s])); 3776 PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s])); 3777 } 3778 for (c = cStart; c < cEnd; ++c) { 3779 const PetscInt cell = cells ? cells[c] : c; 3780 const PetscInt cind = c - cStart; 3781 const PetscInt *cone, *ornt; 3782 3783 PetscCall(DMPlexGetCone(dm, cell, &cone)); 3784 PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt)); 3785 //PetscCheck(!ornt[0], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != 0", cone[0], cell, ornt[0]); 3786 for (s = 0; s < 2; ++s) { 3787 const PetscInt tdX = totDimX[s]; 3788 PetscScalar *closure = NULL, *xl = &x[s][cind * tdX]; 3789 PetscInt face = cone[s], point = face, subpoint, Nx, i; 3790 3791 if (useCell) { 3792 const PetscInt *support; 3793 PetscInt ssize; 3794 3795 PetscCall(DMPlexGetSupport(dm, face, &support)); 3796 PetscCall(DMPlexGetSupportSize(dm, face, &ssize)); 3797 PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", face, cell, ssize); 3798 if (support[0] == cell) point = support[1]; 3799 else if (support[1] == cell) point = support[0]; 3800 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell); 3801 } 3802 PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint)); 3803 PetscCall(DMPlexVecGetOrientedClosure_Internal(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure)); 3804 PetscCheck(Nx == tdX, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for subpoint %" PetscInt_FMT " does not match DS size %" PetscInt_FMT, Nx, subpoint, tdX); 3805 for (i = 0; i < Nx; ++i) xl[i] = closure[i]; 3806 PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure)); 3807 } 3808 } 3809 for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s])); 3810 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 3811 PetscFunctionReturn(PETSC_SUCCESS); 3812 } 3813 3814 static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[]) 3815 { 3816 PetscFunctionBegin; 3817 if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS); 3818 PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0])); 3819 PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1])); 3820 PetscFunctionReturn(PETSC_SUCCESS); 3821 } 3822 3823 /*@C 3824 DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces 3825 3826 Input Parameters: 3827 + dm - The `DM` 3828 . fStart - The first face to include 3829 . fEnd - The first face to exclude 3830 . locX - A local vector with the solution fields 3831 . locX_t - A local vector with solution field time derivatives, or NULL 3832 . faceGeometry - A local vector with face geometry 3833 . cellGeometry - A local vector with cell geometry 3834 - locGrad - A local vector with field gradients, or NULL 3835 3836 Output Parameters: 3837 + Nface - The number of faces with field values 3838 . uL - The field values at the left side of the face 3839 - uR - The field values at the right side of the face 3840 3841 Level: developer 3842 3843 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()` 3844 @*/ 3845 PetscErrorCode DMPlexGetFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR) 3846 { 3847 DM dmFace, dmCell, dmGrad = NULL; 3848 PetscSection section; 3849 PetscDS prob; 3850 DMLabel ghostLabel; 3851 const PetscScalar *facegeom, *cellgeom, *x, *lgrad; 3852 PetscBool *isFE; 3853 PetscInt dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face; 3854 3855 PetscFunctionBegin; 3856 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3857 PetscValidHeaderSpecific(locX, VEC_CLASSID, 4); 3858 if (locX_t) PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5); 3859 PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 6); 3860 PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 7); 3861 if (locGrad) PetscValidHeaderSpecific(locGrad, VEC_CLASSID, 8); 3862 PetscAssertPointer(uL, 10); 3863 PetscAssertPointer(uR, 11); 3864 PetscCall(DMGetDimension(dm, &dim)); 3865 PetscCall(DMGetDS(dm, &prob)); 3866 PetscCall(DMGetLocalSection(dm, §ion)); 3867 PetscCall(PetscDSGetNumFields(prob, &Nf)); 3868 PetscCall(PetscDSGetTotalComponents(prob, &Nc)); 3869 PetscCall(PetscMalloc1(Nf, &isFE)); 3870 for (f = 0; f < Nf; ++f) { 3871 PetscObject obj; 3872 PetscClassId id; 3873 3874 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 3875 PetscCall(PetscObjectGetClassId(obj, &id)); 3876 if (id == PETSCFE_CLASSID) { 3877 isFE[f] = PETSC_TRUE; 3878 } else if (id == PETSCFV_CLASSID) { 3879 isFE[f] = PETSC_FALSE; 3880 } else { 3881 isFE[f] = PETSC_FALSE; 3882 } 3883 } 3884 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 3885 PetscCall(VecGetArrayRead(locX, &x)); 3886 PetscCall(VecGetDM(faceGeometry, &dmFace)); 3887 PetscCall(VecGetArrayRead(faceGeometry, &facegeom)); 3888 PetscCall(VecGetDM(cellGeometry, &dmCell)); 3889 PetscCall(VecGetArrayRead(cellGeometry, &cellgeom)); 3890 if (locGrad) { 3891 PetscCall(VecGetDM(locGrad, &dmGrad)); 3892 PetscCall(VecGetArrayRead(locGrad, &lgrad)); 3893 } 3894 PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL)); 3895 PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR)); 3896 /* Right now just eat the extra work for FE (could make a cell loop) */ 3897 for (face = fStart, iface = 0; face < fEnd; ++face) { 3898 const PetscInt *cells; 3899 PetscFVFaceGeom *fg; 3900 PetscFVCellGeom *cgL, *cgR; 3901 PetscScalar *xL, *xR, *gL, *gR; 3902 PetscScalar *uLl = *uL, *uRl = *uR; 3903 PetscInt ghost, nsupp, nchild; 3904 3905 PetscCall(DMLabelGetValue(ghostLabel, face, &ghost)); 3906 PetscCall(DMPlexGetSupportSize(dm, face, &nsupp)); 3907 PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL)); 3908 if (ghost >= 0 || nsupp > 2 || nchild > 0) continue; 3909 PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg)); 3910 PetscCall(DMPlexGetSupport(dm, face, &cells)); 3911 PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL)); 3912 PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR)); 3913 for (f = 0; f < Nf; ++f) { 3914 PetscInt off; 3915 3916 PetscCall(PetscDSGetComponentOffset(prob, f, &off)); 3917 if (isFE[f]) { 3918 const PetscInt *cone; 3919 PetscInt comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d; 3920 3921 xL = xR = NULL; 3922 PetscCall(PetscSectionGetFieldComponents(section, f, &comp)); 3923 PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL)); 3924 PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR)); 3925 PetscCall(DMPlexGetCone(dm, cells[0], &cone)); 3926 PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL)); 3927 for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL) 3928 if (cone[faceLocL] == face) break; 3929 PetscCall(DMPlexGetCone(dm, cells[1], &cone)); 3930 PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR)); 3931 for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR) 3932 if (cone[faceLocR] == face) break; 3933 PetscCheck(faceLocL != coneSizeL || faceLocR != coneSizeR, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find face %" PetscInt_FMT " in cone of cell %" PetscInt_FMT " or cell %" PetscInt_FMT, face, cells[0], cells[1]); 3934 /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */ 3935 /* TODO: this is a hack that might not be right for nonconforming */ 3936 if (faceLocL < coneSizeL) { 3937 PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off])); 3938 if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off])); 3939 else { 3940 for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d]; 3941 } 3942 } else { 3943 PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off])); 3944 PetscCall(PetscSectionGetFieldComponents(section, f, &comp)); 3945 for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d]; 3946 } 3947 PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL)); 3948 PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR)); 3949 } else { 3950 PetscFV fv; 3951 PetscInt numComp, c; 3952 3953 PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv)); 3954 PetscCall(PetscFVGetNumComponents(fv, &numComp)); 3955 PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL)); 3956 PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR)); 3957 if (dmGrad) { 3958 PetscReal dxL[3], dxR[3]; 3959 3960 PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL)); 3961 PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR)); 3962 DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL); 3963 DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR); 3964 for (c = 0; c < numComp; ++c) { 3965 uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL); 3966 uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR); 3967 } 3968 } else { 3969 for (c = 0; c < numComp; ++c) { 3970 uLl[iface * Nc + off + c] = xL[c]; 3971 uRl[iface * Nc + off + c] = xR[c]; 3972 } 3973 } 3974 } 3975 } 3976 ++iface; 3977 } 3978 *Nface = iface; 3979 PetscCall(VecRestoreArrayRead(locX, &x)); 3980 PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom)); 3981 PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom)); 3982 if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad)); 3983 PetscCall(PetscFree(isFE)); 3984 PetscFunctionReturn(PETSC_SUCCESS); 3985 } 3986 3987 /*@C 3988 DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces 3989 3990 Input Parameters: 3991 + dm - The `DM` 3992 . fStart - The first face to include 3993 . fEnd - The first face to exclude 3994 . locX - A local vector with the solution fields 3995 . locX_t - A local vector with solution field time derivatives, or NULL 3996 . faceGeometry - A local vector with face geometry 3997 . cellGeometry - A local vector with cell geometry 3998 - locGrad - A local vector with field gradients, or NULL 3999 4000 Output Parameters: 4001 + Nface - The number of faces with field values 4002 . uL - The field values at the left side of the face 4003 - uR - The field values at the right side of the face 4004 4005 Level: developer 4006 4007 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()` 4008 @*/ 4009 PetscErrorCode DMPlexRestoreFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR) 4010 { 4011 PetscFunctionBegin; 4012 PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL)); 4013 PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR)); 4014 PetscFunctionReturn(PETSC_SUCCESS); 4015 } 4016 4017 /*@C 4018 DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces 4019 4020 Input Parameters: 4021 + dm - The `DM` 4022 . fStart - The first face to include 4023 . fEnd - The first face to exclude 4024 . faceGeometry - A local vector with face geometry 4025 - cellGeometry - A local vector with cell geometry 4026 4027 Output Parameters: 4028 + Nface - The number of faces with field values 4029 . fgeom - The extract the face centroid and normal 4030 - vol - The cell volume 4031 4032 Level: developer 4033 4034 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()` 4035 @*/ 4036 PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol) 4037 { 4038 DM dmFace, dmCell; 4039 DMLabel ghostLabel; 4040 const PetscScalar *facegeom, *cellgeom; 4041 PetscInt dim, numFaces = fEnd - fStart, iface, face; 4042 4043 PetscFunctionBegin; 4044 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 4045 PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 4); 4046 PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 5); 4047 PetscAssertPointer(fgeom, 7); 4048 PetscAssertPointer(vol, 8); 4049 PetscCall(DMGetDimension(dm, &dim)); 4050 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 4051 PetscCall(VecGetDM(faceGeometry, &dmFace)); 4052 PetscCall(VecGetArrayRead(faceGeometry, &facegeom)); 4053 PetscCall(VecGetDM(cellGeometry, &dmCell)); 4054 PetscCall(VecGetArrayRead(cellGeometry, &cellgeom)); 4055 PetscCall(PetscMalloc1(numFaces, fgeom)); 4056 PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol)); 4057 for (face = fStart, iface = 0; face < fEnd; ++face) { 4058 const PetscInt *cells; 4059 PetscFVFaceGeom *fg; 4060 PetscFVCellGeom *cgL, *cgR; 4061 PetscFVFaceGeom *fgeoml = *fgeom; 4062 PetscReal *voll = *vol; 4063 PetscInt ghost, d, nchild, nsupp; 4064 4065 PetscCall(DMLabelGetValue(ghostLabel, face, &ghost)); 4066 PetscCall(DMPlexGetSupportSize(dm, face, &nsupp)); 4067 PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL)); 4068 if (ghost >= 0 || nsupp > 2 || nchild > 0) continue; 4069 PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg)); 4070 PetscCall(DMPlexGetSupport(dm, face, &cells)); 4071 PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL)); 4072 PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR)); 4073 for (d = 0; d < dim; ++d) { 4074 fgeoml[iface].centroid[d] = fg->centroid[d]; 4075 fgeoml[iface].normal[d] = fg->normal[d]; 4076 } 4077 voll[iface * 2 + 0] = cgL->volume; 4078 voll[iface * 2 + 1] = cgR->volume; 4079 ++iface; 4080 } 4081 *Nface = iface; 4082 PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom)); 4083 PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom)); 4084 PetscFunctionReturn(PETSC_SUCCESS); 4085 } 4086 4087 /*@C 4088 DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces 4089 4090 Input Parameters: 4091 + dm - The `DM` 4092 . fStart - The first face to include 4093 . fEnd - The first face to exclude 4094 . faceGeometry - A local vector with face geometry 4095 - cellGeometry - A local vector with cell geometry 4096 4097 Output Parameters: 4098 + Nface - The number of faces with field values 4099 . fgeom - The extract the face centroid and normal 4100 - vol - The cell volume 4101 4102 Level: developer 4103 4104 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()` 4105 @*/ 4106 PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol) 4107 { 4108 PetscFunctionBegin; 4109 PetscCall(PetscFree(*fgeom)); 4110 PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol)); 4111 PetscFunctionReturn(PETSC_SUCCESS); 4112 } 4113 4114 PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom) 4115 { 4116 char composeStr[33] = {0}; 4117 PetscObjectId id; 4118 PetscContainer container; 4119 4120 PetscFunctionBegin; 4121 PetscCall(PetscObjectGetId((PetscObject)quad, &id)); 4122 PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id)); 4123 PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container)); 4124 if (container) { 4125 PetscCall(PetscContainerGetPointer(container, (void **)geom)); 4126 } else { 4127 PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom)); 4128 PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container)); 4129 PetscCall(PetscContainerSetPointer(container, (void *)*geom)); 4130 PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom)); 4131 PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container)); 4132 PetscCall(PetscContainerDestroy(&container)); 4133 } 4134 PetscFunctionReturn(PETSC_SUCCESS); 4135 } 4136 4137 PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom) 4138 { 4139 PetscFunctionBegin; 4140 *geom = NULL; 4141 PetscFunctionReturn(PETSC_SUCCESS); 4142 } 4143 4144 PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user) 4145 { 4146 DM_Plex *mesh = (DM_Plex *)dm->data; 4147 const char *name = "Residual"; 4148 DM dmAux = NULL; 4149 DMLabel ghostLabel = NULL; 4150 PetscDS prob = NULL; 4151 PetscDS probAux = NULL; 4152 PetscBool useFEM = PETSC_FALSE; 4153 PetscBool isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE; 4154 DMField coordField = NULL; 4155 Vec locA; 4156 PetscScalar *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL; 4157 IS chunkIS; 4158 const PetscInt *cells; 4159 PetscInt cStart, cEnd, numCells; 4160 PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd; 4161 PetscInt maxDegree = PETSC_MAX_INT; 4162 PetscFormKey key; 4163 PetscQuadrature affineQuad = NULL, *quads = NULL; 4164 PetscFEGeom *affineGeom = NULL, **geoms = NULL; 4165 4166 PetscFunctionBegin; 4167 PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 4168 /* FEM+FVM */ 4169 /* 1: Get sizes from dm and dmAux */ 4170 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 4171 PetscCall(DMGetDS(dm, &prob)); 4172 PetscCall(PetscDSGetNumFields(prob, &Nf)); 4173 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 4174 PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA)); 4175 if (locA) { 4176 PetscCall(VecGetDM(locA, &dmAux)); 4177 PetscCall(DMGetDS(dmAux, &probAux)); 4178 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 4179 } 4180 /* 2: Get geometric data */ 4181 for (f = 0; f < Nf; ++f) { 4182 PetscObject obj; 4183 PetscClassId id; 4184 PetscBool fimp; 4185 4186 PetscCall(PetscDSGetImplicit(prob, f, &fimp)); 4187 if (isImplicit != fimp) continue; 4188 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 4189 PetscCall(PetscObjectGetClassId(obj, &id)); 4190 if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE; 4191 PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented"); 4192 } 4193 if (useFEM) { 4194 PetscCall(DMGetCoordinateField(dm, &coordField)); 4195 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 4196 if (maxDegree <= 1) { 4197 PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad)); 4198 if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 4199 } else { 4200 PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms)); 4201 for (f = 0; f < Nf; ++f) { 4202 PetscObject obj; 4203 PetscClassId id; 4204 PetscBool fimp; 4205 4206 PetscCall(PetscDSGetImplicit(prob, f, &fimp)); 4207 if (isImplicit != fimp) continue; 4208 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 4209 PetscCall(PetscObjectGetClassId(obj, &id)); 4210 if (id == PETSCFE_CLASSID) { 4211 PetscFE fe = (PetscFE)obj; 4212 4213 PetscCall(PetscFEGetQuadrature(fe, &quads[f])); 4214 PetscCall(PetscObjectReference((PetscObject)quads[f])); 4215 PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 4216 } 4217 } 4218 } 4219 } 4220 /* Loop over chunks */ 4221 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 4222 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 4223 if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS)); 4224 numCells = cEnd - cStart; 4225 numChunks = 1; 4226 cellChunkSize = numCells / numChunks; 4227 numChunks = PetscMin(1, numCells); 4228 key.label = NULL; 4229 key.value = 0; 4230 key.part = 0; 4231 for (chunk = 0; chunk < numChunks; ++chunk) { 4232 PetscScalar *elemVec, *fluxL = NULL, *fluxR = NULL; 4233 PetscReal *vol = NULL; 4234 PetscFVFaceGeom *fgeom = NULL; 4235 PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c; 4236 PetscInt numFaces = 0; 4237 4238 /* Extract field coefficients */ 4239 if (useFEM) { 4240 PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells)); 4241 PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a)); 4242 PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec)); 4243 PetscCall(PetscArrayzero(elemVec, numCells * totDim)); 4244 } 4245 /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */ 4246 /* Loop over fields */ 4247 for (f = 0; f < Nf; ++f) { 4248 PetscObject obj; 4249 PetscClassId id; 4250 PetscBool fimp; 4251 PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset; 4252 4253 key.field = f; 4254 PetscCall(PetscDSGetImplicit(prob, f, &fimp)); 4255 if (isImplicit != fimp) continue; 4256 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 4257 PetscCall(PetscObjectGetClassId(obj, &id)); 4258 if (id == PETSCFE_CLASSID) { 4259 PetscFE fe = (PetscFE)obj; 4260 PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f]; 4261 PetscFEGeom *chunkGeom = NULL; 4262 PetscQuadrature quad = affineQuad ? affineQuad : quads[f]; 4263 PetscInt Nq, Nb; 4264 4265 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 4266 PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL)); 4267 PetscCall(PetscFEGetDimension(fe, &Nb)); 4268 blockSize = Nb; 4269 batchSize = numBlocks * blockSize; 4270 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 4271 numChunks = numCells / (numBatches * batchSize); 4272 Ne = numChunks * numBatches * batchSize; 4273 Nr = numCells % (numBatches * batchSize); 4274 offset = numCells - Nr; 4275 /* Integrate FE residual to get elemVec (need fields at quadrature points) */ 4276 /* For FV, I think we use a P0 basis and the cell coefficients (for subdivided cells, we can tweak the basis tabulation to be the indicator function) */ 4277 PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom)); 4278 PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec)); 4279 PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom)); 4280 PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim])); 4281 PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom)); 4282 } else if (id == PETSCFV_CLASSID) { 4283 PetscFV fv = (PetscFV)obj; 4284 4285 Ne = numFaces; 4286 /* Riemann solve over faces (need fields at face centroids) */ 4287 /* We need to evaluate FE fields at those coordinates */ 4288 PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR)); 4289 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f); 4290 } 4291 /* Loop over domain */ 4292 if (useFEM) { 4293 /* Add elemVec to locX */ 4294 for (c = cS; c < cE; ++c) { 4295 const PetscInt cell = cells ? cells[c] : c; 4296 const PetscInt cind = c - cStart; 4297 4298 if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim])); 4299 if (ghostLabel) { 4300 PetscInt ghostVal; 4301 4302 PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal)); 4303 if (ghostVal > 0) continue; 4304 } 4305 PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES)); 4306 } 4307 } 4308 /* Handle time derivative */ 4309 if (locX_t) { 4310 PetscScalar *x_t, *fa; 4311 4312 PetscCall(VecGetArray(locF, &fa)); 4313 PetscCall(VecGetArray(locX_t, &x_t)); 4314 for (f = 0; f < Nf; ++f) { 4315 PetscFV fv; 4316 PetscObject obj; 4317 PetscClassId id; 4318 PetscInt pdim, d; 4319 4320 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 4321 PetscCall(PetscObjectGetClassId(obj, &id)); 4322 if (id != PETSCFV_CLASSID) continue; 4323 fv = (PetscFV)obj; 4324 PetscCall(PetscFVGetNumComponents(fv, &pdim)); 4325 for (c = cS; c < cE; ++c) { 4326 const PetscInt cell = cells ? cells[c] : c; 4327 PetscScalar *u_t, *r; 4328 4329 if (ghostLabel) { 4330 PetscInt ghostVal; 4331 4332 PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal)); 4333 if (ghostVal > 0) continue; 4334 } 4335 PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t)); 4336 PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r)); 4337 for (d = 0; d < pdim; ++d) r[d] += u_t[d]; 4338 } 4339 } 4340 PetscCall(VecRestoreArray(locX_t, &x_t)); 4341 PetscCall(VecRestoreArray(locF, &fa)); 4342 } 4343 if (useFEM) { 4344 PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a)); 4345 PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec)); 4346 } 4347 } 4348 if (useFEM) PetscCall(ISDestroy(&chunkIS)); 4349 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 4350 /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */ 4351 if (useFEM) { 4352 if (maxDegree <= 1) { 4353 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 4354 PetscCall(PetscQuadratureDestroy(&affineQuad)); 4355 } else { 4356 for (f = 0; f < Nf; ++f) { 4357 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 4358 PetscCall(PetscQuadratureDestroy(&quads[f])); 4359 } 4360 PetscCall(PetscFree2(quads, geoms)); 4361 } 4362 } 4363 PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 4364 PetscFunctionReturn(PETSC_SUCCESS); 4365 } 4366 4367 /* 4368 We always assemble JacP, and if the matrix is different from Jac and two different sets of point functions are provided, we also assemble Jac 4369 4370 X - The local solution vector 4371 X_t - The local solution time derivative vector, or NULL 4372 */ 4373 PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM dm, PetscSection section, PetscSection globalSection, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *ctx) 4374 { 4375 DM_Plex *mesh = (DM_Plex *)dm->data; 4376 const char *name = "Jacobian", *nameP = "JacobianPre"; 4377 DM dmAux = NULL; 4378 PetscDS prob, probAux = NULL; 4379 PetscSection sectionAux = NULL; 4380 Vec A; 4381 DMField coordField; 4382 PetscFEGeom *cgeomFEM; 4383 PetscQuadrature qGeom = NULL; 4384 Mat J = Jac, JP = JacP; 4385 PetscScalar *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL; 4386 PetscBool hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE; 4387 const PetscInt *cells; 4388 PetscFormKey key; 4389 PetscInt Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0; 4390 4391 PetscFunctionBegin; 4392 PetscCall(ISGetLocalSize(cellIS, &numCells)); 4393 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 4394 PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 4395 PetscCall(DMGetDS(dm, &prob)); 4396 PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A)); 4397 if (A) { 4398 PetscCall(VecGetDM(A, &dmAux)); 4399 PetscCall(DMGetLocalSection(dmAux, §ionAux)); 4400 PetscCall(DMGetDS(dmAux, &probAux)); 4401 } 4402 /* Get flags */ 4403 PetscCall(PetscDSGetNumFields(prob, &Nf)); 4404 PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE)); 4405 for (fieldI = 0; fieldI < Nf; ++fieldI) { 4406 PetscObject disc; 4407 PetscClassId id; 4408 PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc)); 4409 PetscCall(PetscObjectGetClassId(disc, &id)); 4410 if (id == PETSCFE_CLASSID) { 4411 isFE[fieldI] = PETSC_TRUE; 4412 } else if (id == PETSCFV_CLASSID) { 4413 hasFV = PETSC_TRUE; 4414 isFE[fieldI] = PETSC_FALSE; 4415 } 4416 } 4417 PetscCall(PetscDSHasJacobian(prob, &hasJac)); 4418 PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec)); 4419 PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn)); 4420 assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE; 4421 hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE; 4422 if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */ 4423 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 4424 if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 4425 /* Compute batch sizes */ 4426 if (isFE[0]) { 4427 PetscFE fe; 4428 PetscQuadrature q; 4429 PetscInt numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb; 4430 4431 PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe)); 4432 PetscCall(PetscFEGetQuadrature(fe, &q)); 4433 PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL)); 4434 PetscCall(PetscFEGetDimension(fe, &Nb)); 4435 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 4436 blockSize = Nb * numQuadPoints; 4437 batchSize = numBlocks * blockSize; 4438 chunkSize = numBatches * batchSize; 4439 numChunks = numCells / chunkSize + numCells % chunkSize; 4440 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 4441 } else { 4442 chunkSize = numCells; 4443 numChunks = 1; 4444 } 4445 /* Get work space */ 4446 wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize; 4447 PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work)); 4448 PetscCall(PetscArrayzero(work, wsz)); 4449 off = 0; 4450 u = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL; 4451 u_t = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL; 4452 a = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL; 4453 elemMat = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL; 4454 elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL; 4455 elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL; 4456 PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz); 4457 /* Setup geometry */ 4458 PetscCall(DMGetCoordinateField(dm, &coordField)); 4459 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 4460 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom)); 4461 if (!qGeom) { 4462 PetscFE fe; 4463 4464 PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe)); 4465 PetscCall(PetscFEGetQuadrature(fe, &qGeom)); 4466 PetscCall(PetscObjectReference((PetscObject)qGeom)); 4467 } 4468 PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 4469 /* Compute volume integrals */ 4470 if (assembleJac) PetscCall(MatZeroEntries(J)); 4471 PetscCall(MatZeroEntries(JP)); 4472 key.label = NULL; 4473 key.value = 0; 4474 key.part = 0; 4475 for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) { 4476 const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell); 4477 PetscInt c; 4478 4479 /* Extract values */ 4480 for (c = 0; c < Ncell; ++c) { 4481 const PetscInt cell = cells ? cells[c + offCell] : c + offCell; 4482 PetscScalar *x = NULL, *x_t = NULL; 4483 PetscInt i; 4484 4485 if (X) { 4486 PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x)); 4487 for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i]; 4488 PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x)); 4489 } 4490 if (X_t) { 4491 PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t)); 4492 for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i]; 4493 PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t)); 4494 } 4495 if (dmAux) { 4496 PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x)); 4497 for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i]; 4498 PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x)); 4499 } 4500 } 4501 for (fieldI = 0; fieldI < Nf; ++fieldI) { 4502 PetscFE fe; 4503 PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe)); 4504 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 4505 key.field = fieldI * Nf + fieldJ; 4506 if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat)); 4507 if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP)); 4508 if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD)); 4509 } 4510 /* For finite volume, add the identity */ 4511 if (!isFE[fieldI]) { 4512 PetscFV fv; 4513 PetscInt eOffset = 0, Nc, fc, foff; 4514 4515 PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff)); 4516 PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv)); 4517 PetscCall(PetscFVGetNumComponents(fv, &Nc)); 4518 for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) { 4519 for (fc = 0; fc < Nc; ++fc) { 4520 const PetscInt i = foff + fc; 4521 if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0; 4522 if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0; 4523 } 4524 } 4525 } 4526 } 4527 /* Add contribution from X_t */ 4528 if (hasDyn) { 4529 for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c]; 4530 } 4531 /* Insert values into matrix */ 4532 for (c = 0; c < Ncell; ++c) { 4533 const PetscInt cell = cells ? cells[c + offCell] : c + offCell; 4534 if (mesh->printFEM > 1) { 4535 if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim])); 4536 if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim])); 4537 } 4538 if (assembleJac) PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES)); 4539 PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES)); 4540 } 4541 } 4542 /* Cleanup */ 4543 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 4544 PetscCall(PetscQuadratureDestroy(&qGeom)); 4545 if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE)); 4546 PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE)); 4547 PetscCall(DMRestoreWorkArray(dm, ((1 + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize, MPIU_SCALAR, &work)); 4548 /* Compute boundary integrals */ 4549 /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */ 4550 /* Assemble matrix */ 4551 if (assembleJac) { 4552 PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY)); 4553 PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY)); 4554 } 4555 PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY)); 4556 PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY)); 4557 PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 4558 PetscFunctionReturn(PETSC_SUCCESS); 4559 } 4560 4561 /******** FEM Assembly Function ********/ 4562 4563 static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy) 4564 { 4565 PetscBool isPlex; 4566 4567 PetscFunctionBegin; 4568 PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex)); 4569 if (isPlex) { 4570 *plex = dm; 4571 PetscCall(PetscObjectReference((PetscObject)dm)); 4572 } else { 4573 PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex)); 4574 if (!*plex) { 4575 PetscCall(DMConvert(dm, DMPLEX, plex)); 4576 PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex)); 4577 if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex)); 4578 } else { 4579 PetscCall(PetscObjectReference((PetscObject)*plex)); 4580 } 4581 } 4582 PetscFunctionReturn(PETSC_SUCCESS); 4583 } 4584 4585 /*@ 4586 DMPlexGetGeometryFVM - Return precomputed geometric data 4587 4588 Collective 4589 4590 Input Parameter: 4591 . dm - The `DM` 4592 4593 Output Parameters: 4594 + facegeom - The values precomputed from face geometry 4595 . cellgeom - The values precomputed from cell geometry 4596 - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell 4597 4598 Level: developer 4599 4600 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()` 4601 @*/ 4602 PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius) 4603 { 4604 DM plex; 4605 4606 PetscFunctionBegin; 4607 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 4608 PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE)); 4609 PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL)); 4610 if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius)); 4611 PetscCall(DMDestroy(&plex)); 4612 PetscFunctionReturn(PETSC_SUCCESS); 4613 } 4614 4615 /*@ 4616 DMPlexGetGradientDM - Return gradient data layout 4617 4618 Collective 4619 4620 Input Parameters: 4621 + dm - The `DM` 4622 - fv - The `PetscFV` 4623 4624 Output Parameter: 4625 . dmGrad - The layout for gradient values 4626 4627 Level: developer 4628 4629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()` 4630 @*/ 4631 PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad) 4632 { 4633 DM plex; 4634 PetscBool computeGradients; 4635 4636 PetscFunctionBegin; 4637 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 4638 PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2); 4639 PetscAssertPointer(dmGrad, 3); 4640 PetscCall(PetscFVGetComputeGradients(fv, &computeGradients)); 4641 if (!computeGradients) { 4642 *dmGrad = NULL; 4643 PetscFunctionReturn(PETSC_SUCCESS); 4644 } 4645 PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE)); 4646 PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad)); 4647 PetscCall(DMDestroy(&plex)); 4648 PetscFunctionReturn(PETSC_SUCCESS); 4649 } 4650 4651 static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS) 4652 { 4653 DM_Plex *mesh = (DM_Plex *)dm->data; 4654 DM plex = NULL, plexA = NULL; 4655 DMEnclosureType encAux; 4656 PetscDS prob, probAux = NULL; 4657 PetscSection section, sectionAux = NULL; 4658 Vec locA = NULL; 4659 PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemVec = NULL; 4660 PetscInt totDim, totDimAux = 0; 4661 4662 PetscFunctionBegin; 4663 PetscCall(DMConvert(dm, DMPLEX, &plex)); 4664 PetscCall(DMGetLocalSection(dm, §ion)); 4665 PetscCall(DMGetDS(dm, &prob)); 4666 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 4667 PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA)); 4668 if (locA) { 4669 DM dmAux; 4670 4671 PetscCall(VecGetDM(locA, &dmAux)); 4672 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 4673 PetscCall(DMConvert(dmAux, DMPLEX, &plexA)); 4674 PetscCall(DMGetDS(plexA, &probAux)); 4675 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 4676 PetscCall(DMGetLocalSection(plexA, §ionAux)); 4677 } 4678 { 4679 PetscFEGeom *fgeom; 4680 PetscInt maxDegree; 4681 PetscQuadrature qGeom = NULL; 4682 IS pointIS; 4683 const PetscInt *points; 4684 PetscInt numFaces, face, Nq; 4685 4686 PetscCall(DMLabelGetStratumIS(key.label, key.value, &pointIS)); 4687 if (!pointIS) goto end; /* No points with that id on this process */ 4688 { 4689 IS isectIS; 4690 4691 /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */ 4692 PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS)); 4693 PetscCall(ISDestroy(&pointIS)); 4694 pointIS = isectIS; 4695 } 4696 PetscCall(ISGetLocalSize(pointIS, &numFaces)); 4697 PetscCall(ISGetIndices(pointIS, &points)); 4698 PetscCall(PetscMalloc4(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, numFaces * totDim, &elemVec, locA ? numFaces * totDimAux : 0, &a)); 4699 PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree)); 4700 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom)); 4701 if (!qGeom) { 4702 PetscFE fe; 4703 4704 PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe)); 4705 PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom)); 4706 PetscCall(PetscObjectReference((PetscObject)qGeom)); 4707 } 4708 PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL)); 4709 PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom)); 4710 for (face = 0; face < numFaces; ++face) { 4711 const PetscInt point = points[face], *support; 4712 PetscScalar *x = NULL; 4713 PetscInt i; 4714 4715 PetscCall(DMPlexGetSupport(dm, point, &support)); 4716 PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x)); 4717 for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i]; 4718 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x)); 4719 if (locX_t) { 4720 PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x)); 4721 for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i]; 4722 PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x)); 4723 } 4724 if (locA) { 4725 PetscInt subp; 4726 4727 PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp)); 4728 PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x)); 4729 for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i]; 4730 PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x)); 4731 } 4732 } 4733 PetscCall(PetscArrayzero(elemVec, numFaces * totDim)); 4734 { 4735 PetscFE fe; 4736 PetscInt Nb; 4737 PetscFEGeom *chunkGeom = NULL; 4738 /* Conforming batches */ 4739 PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize; 4740 /* Remainder */ 4741 PetscInt Nr, offset; 4742 4743 PetscCall(PetscDSGetDiscretization(prob, key.field, (PetscObject *)&fe)); 4744 PetscCall(PetscFEGetDimension(fe, &Nb)); 4745 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 4746 /* TODO: documentation is unclear about what is going on with these numbers: how should Nb / Nq factor in ? */ 4747 blockSize = Nb; 4748 batchSize = numBlocks * blockSize; 4749 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 4750 numChunks = numFaces / (numBatches * batchSize); 4751 Ne = numChunks * numBatches * batchSize; 4752 Nr = numFaces % (numBatches * batchSize); 4753 offset = numFaces - Nr; 4754 PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom)); 4755 PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec)); 4756 PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom)); 4757 PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom)); 4758 PetscCall(PetscFEIntegrateBdResidual(prob, wf, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, &elemVec[offset * totDim])); 4759 PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom)); 4760 } 4761 for (face = 0; face < numFaces; ++face) { 4762 const PetscInt point = points[face], *support; 4763 4764 if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(point, "BdResidual", totDim, &elemVec[face * totDim])); 4765 PetscCall(DMPlexGetSupport(plex, point, &support)); 4766 PetscCall(DMPlexVecSetClosure(plex, NULL, locF, support[0], &elemVec[face * totDim], ADD_ALL_VALUES)); 4767 } 4768 PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom)); 4769 PetscCall(PetscQuadratureDestroy(&qGeom)); 4770 PetscCall(ISRestoreIndices(pointIS, &points)); 4771 PetscCall(ISDestroy(&pointIS)); 4772 PetscCall(PetscFree4(u, u_t, elemVec, a)); 4773 } 4774 end: 4775 PetscCall(DMDestroy(&plex)); 4776 PetscCall(DMDestroy(&plexA)); 4777 PetscFunctionReturn(PETSC_SUCCESS); 4778 } 4779 4780 PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF) 4781 { 4782 DMField coordField; 4783 DMLabel depthLabel; 4784 IS facetIS; 4785 PetscInt dim; 4786 4787 PetscFunctionBegin; 4788 PetscCall(DMGetDimension(dm, &dim)); 4789 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 4790 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 4791 PetscCall(DMGetCoordinateField(dm, &coordField)); 4792 PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS)); 4793 PetscCall(ISDestroy(&facetIS)); 4794 PetscFunctionReturn(PETSC_SUCCESS); 4795 } 4796 4797 static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user) 4798 { 4799 PetscDS prob; 4800 PetscInt numBd, bd; 4801 DMField coordField = NULL; 4802 IS facetIS = NULL; 4803 DMLabel depthLabel; 4804 PetscInt dim; 4805 4806 PetscFunctionBegin; 4807 PetscCall(DMGetDS(dm, &prob)); 4808 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 4809 PetscCall(DMGetDimension(dm, &dim)); 4810 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 4811 PetscCall(PetscDSGetNumBoundary(prob, &numBd)); 4812 for (bd = 0; bd < numBd; ++bd) { 4813 PetscWeakForm wf; 4814 DMBoundaryConditionType type; 4815 DMLabel label; 4816 const PetscInt *values; 4817 PetscInt field, numValues, v; 4818 PetscObject obj; 4819 PetscClassId id; 4820 PetscFormKey key; 4821 4822 PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL)); 4823 if (type & DM_BC_ESSENTIAL) continue; 4824 PetscCall(PetscDSGetDiscretization(prob, field, &obj)); 4825 PetscCall(PetscObjectGetClassId(obj, &id)); 4826 if (id != PETSCFE_CLASSID) continue; 4827 if (!facetIS) { 4828 DMLabel depthLabel; 4829 PetscInt dim; 4830 4831 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 4832 PetscCall(DMGetDimension(dm, &dim)); 4833 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 4834 } 4835 PetscCall(DMGetCoordinateField(dm, &coordField)); 4836 for (v = 0; v < numValues; ++v) { 4837 key.label = label; 4838 key.value = values[v]; 4839 key.field = field; 4840 key.part = 0; 4841 PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS)); 4842 } 4843 } 4844 PetscCall(ISDestroy(&facetIS)); 4845 PetscFunctionReturn(PETSC_SUCCESS); 4846 } 4847 4848 PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user) 4849 { 4850 DM_Plex *mesh = (DM_Plex *)dm->data; 4851 const char *name = "Residual"; 4852 DM dmAux = NULL; 4853 DM dmGrad = NULL; 4854 DMLabel ghostLabel = NULL; 4855 PetscDS ds = NULL; 4856 PetscDS dsAux = NULL; 4857 PetscSection section = NULL; 4858 PetscBool useFEM = PETSC_FALSE; 4859 PetscBool useFVM = PETSC_FALSE; 4860 PetscBool isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE; 4861 PetscFV fvm = NULL; 4862 DMField coordField = NULL; 4863 Vec locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL; 4864 PetscScalar *u = NULL, *u_t, *a, *uL, *uR; 4865 IS chunkIS; 4866 const PetscInt *cells; 4867 PetscInt cStart, cEnd, numCells; 4868 PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd; 4869 PetscInt maxDegree = PETSC_MAX_INT; 4870 PetscQuadrature affineQuad = NULL, *quads = NULL; 4871 PetscFEGeom *affineGeom = NULL, **geoms = NULL; 4872 4873 PetscFunctionBegin; 4874 if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS); 4875 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 4876 if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS); 4877 PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 4878 /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */ 4879 /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */ 4880 /* FEM+FVM */ 4881 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 4882 /* 1: Get sizes from dm and dmAux */ 4883 PetscCall(DMGetLocalSection(dm, §ion)); 4884 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 4885 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL)); 4886 PetscCall(PetscDSGetNumFields(ds, &Nf)); 4887 PetscCall(PetscDSGetTotalDimension(ds, &totDim)); 4888 PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA)); 4889 if (locA) { 4890 PetscInt subcell; 4891 PetscCall(VecGetDM(locA, &dmAux)); 4892 PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell)); 4893 PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL)); 4894 PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux)); 4895 } 4896 /* 2: Get geometric data */ 4897 for (f = 0; f < Nf; ++f) { 4898 PetscObject obj; 4899 PetscClassId id; 4900 PetscBool fimp; 4901 4902 PetscCall(PetscDSGetImplicit(ds, f, &fimp)); 4903 if (isImplicit != fimp) continue; 4904 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 4905 PetscCall(PetscObjectGetClassId(obj, &id)); 4906 if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE; 4907 if (id == PETSCFV_CLASSID) { 4908 useFVM = PETSC_TRUE; 4909 fvm = (PetscFV)obj; 4910 } 4911 } 4912 if (useFEM) { 4913 PetscCall(DMGetCoordinateField(dm, &coordField)); 4914 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 4915 if (maxDegree <= 1) { 4916 PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad)); 4917 if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 4918 } else { 4919 PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms)); 4920 for (f = 0; f < Nf; ++f) { 4921 PetscObject obj; 4922 PetscClassId id; 4923 PetscBool fimp; 4924 4925 PetscCall(PetscDSGetImplicit(ds, f, &fimp)); 4926 if (isImplicit != fimp) continue; 4927 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 4928 PetscCall(PetscObjectGetClassId(obj, &id)); 4929 if (id == PETSCFE_CLASSID) { 4930 PetscFE fe = (PetscFE)obj; 4931 4932 PetscCall(PetscFEGetQuadrature(fe, &quads[f])); 4933 PetscCall(PetscObjectReference((PetscObject)quads[f])); 4934 PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 4935 } 4936 } 4937 } 4938 } 4939 // Handle non-essential (e.g. outflow) boundary values 4940 if (useFVM) { 4941 PetscCall(DMPlexInsertBoundaryValuesFVM(dm, fvm, locX, time, &locGrad)); 4942 PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL)); 4943 PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad)); 4944 } 4945 /* Loop over chunks */ 4946 if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS)); 4947 numCells = cEnd - cStart; 4948 numChunks = 1; 4949 cellChunkSize = numCells / numChunks; 4950 faceChunkSize = (fEnd - fStart) / numChunks; 4951 numChunks = PetscMin(1, numCells); 4952 for (chunk = 0; chunk < numChunks; ++chunk) { 4953 PetscScalar *elemVec, *fluxL, *fluxR; 4954 PetscReal *vol; 4955 PetscFVFaceGeom *fgeom; 4956 PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c; 4957 PetscInt fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face; 4958 4959 /* Extract field coefficients */ 4960 if (useFEM) { 4961 PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells)); 4962 PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a)); 4963 PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec)); 4964 PetscCall(PetscArrayzero(elemVec, numCells * totDim)); 4965 } 4966 if (useFVM) { 4967 PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR)); 4968 PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol)); 4969 PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL)); 4970 PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR)); 4971 PetscCall(PetscArrayzero(fluxL, numFaces * totDim)); 4972 PetscCall(PetscArrayzero(fluxR, numFaces * totDim)); 4973 } 4974 /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */ 4975 /* Loop over fields */ 4976 for (f = 0; f < Nf; ++f) { 4977 PetscObject obj; 4978 PetscClassId id; 4979 PetscBool fimp; 4980 PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset; 4981 4982 key.field = f; 4983 PetscCall(PetscDSGetImplicit(ds, f, &fimp)); 4984 if (isImplicit != fimp) continue; 4985 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 4986 PetscCall(PetscObjectGetClassId(obj, &id)); 4987 if (id == PETSCFE_CLASSID) { 4988 PetscFE fe = (PetscFE)obj; 4989 PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f]; 4990 PetscFEGeom *chunkGeom = NULL; 4991 PetscQuadrature quad = affineQuad ? affineQuad : quads[f]; 4992 PetscInt Nq, Nb; 4993 4994 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 4995 PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL)); 4996 PetscCall(PetscFEGetDimension(fe, &Nb)); 4997 blockSize = Nb; 4998 batchSize = numBlocks * blockSize; 4999 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 5000 numChunks = numCells / (numBatches * batchSize); 5001 Ne = numChunks * numBatches * batchSize; 5002 Nr = numCells % (numBatches * batchSize); 5003 offset = numCells - Nr; 5004 /* Integrate FE residual to get elemVec (need fields at quadrature points) */ 5005 /* For FV, I think we use a P0 basis and the cell coefficients (for subdivided cells, we can tweak the basis tabulation to be the indicator function) */ 5006 PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom)); 5007 PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec)); 5008 PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom)); 5009 PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux, a ? &a[offset * totDimAux] : NULL, t, &elemVec[offset * totDim])); 5010 PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom)); 5011 } else if (id == PETSCFV_CLASSID) { 5012 PetscFV fv = (PetscFV)obj; 5013 5014 Ne = numFaces; 5015 /* Riemann solve over faces (need fields at face centroids) */ 5016 /* We need to evaluate FE fields at those coordinates */ 5017 PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR)); 5018 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f); 5019 } 5020 /* Loop over domain */ 5021 if (useFEM) { 5022 /* Add elemVec to locX */ 5023 for (c = cS; c < cE; ++c) { 5024 const PetscInt cell = cells ? cells[c] : c; 5025 const PetscInt cind = c - cStart; 5026 5027 if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim])); 5028 if (ghostLabel) { 5029 PetscInt ghostVal; 5030 5031 PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal)); 5032 if (ghostVal > 0) continue; 5033 } 5034 PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES)); 5035 } 5036 } 5037 if (useFVM) { 5038 PetscScalar *fa; 5039 PetscInt iface; 5040 5041 PetscCall(VecGetArray(locF, &fa)); 5042 for (f = 0; f < Nf; ++f) { 5043 PetscFV fv; 5044 PetscObject obj; 5045 PetscClassId id; 5046 PetscInt cdim, foff, pdim; 5047 5048 PetscCall(DMGetCoordinateDim(dm, &cdim)); 5049 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 5050 PetscCall(PetscDSGetFieldOffset(ds, f, &foff)); 5051 PetscCall(PetscObjectGetClassId(obj, &id)); 5052 if (id != PETSCFV_CLASSID) continue; 5053 fv = (PetscFV)obj; 5054 PetscCall(PetscFVGetNumComponents(fv, &pdim)); 5055 /* Accumulate fluxes to cells */ 5056 for (face = fS, iface = 0; face < fE; ++face) { 5057 const PetscInt *scells; 5058 PetscScalar *fL = NULL, *fR = NULL; 5059 PetscInt ghost, d, nsupp, nchild; 5060 5061 PetscCall(DMLabelGetValue(ghostLabel, face, &ghost)); 5062 PetscCall(DMPlexGetSupportSize(dm, face, &nsupp)); 5063 PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL)); 5064 if (ghost >= 0 || nsupp > 2 || nchild > 0) continue; 5065 PetscCall(DMPlexGetSupport(dm, face, &scells)); 5066 PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost)); 5067 if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL)); 5068 PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost)); 5069 if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR)); 5070 if (mesh->printFVM > 1) { 5071 PetscCall(DMPrintCellVectorReal(face, "Residual: normal", cdim, fgeom[iface].normal)); 5072 PetscCall(DMPrintCellVector(face, "Residual: left state", pdim, &uL[iface * totDim + foff])); 5073 PetscCall(DMPrintCellVector(face, "Residual: right state", pdim, &uR[iface * totDim + foff])); 5074 PetscCall(DMPrintCellVector(face, "Residual: left flux", pdim, &fluxL[iface * totDim + foff])); 5075 PetscCall(DMPrintCellVector(face, "Residual: right flux", pdim, &fluxR[iface * totDim + foff])); 5076 } 5077 for (d = 0; d < pdim; ++d) { 5078 if (fL) fL[d] -= fluxL[iface * totDim + foff + d]; 5079 if (fR) fR[d] += fluxR[iface * totDim + foff + d]; 5080 } 5081 ++iface; 5082 } 5083 } 5084 PetscCall(VecRestoreArray(locF, &fa)); 5085 } 5086 /* Handle time derivative */ 5087 if (locX_t) { 5088 PetscScalar *x_t, *fa; 5089 5090 PetscCall(VecGetArray(locF, &fa)); 5091 PetscCall(VecGetArray(locX_t, &x_t)); 5092 for (f = 0; f < Nf; ++f) { 5093 PetscFV fv; 5094 PetscObject obj; 5095 PetscClassId id; 5096 PetscInt pdim, d; 5097 5098 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 5099 PetscCall(PetscObjectGetClassId(obj, &id)); 5100 if (id != PETSCFV_CLASSID) continue; 5101 fv = (PetscFV)obj; 5102 PetscCall(PetscFVGetNumComponents(fv, &pdim)); 5103 for (c = cS; c < cE; ++c) { 5104 const PetscInt cell = cells ? cells[c] : c; 5105 PetscScalar *u_t, *r; 5106 5107 if (ghostLabel) { 5108 PetscInt ghostVal; 5109 5110 PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal)); 5111 if (ghostVal > 0) continue; 5112 } 5113 PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t)); 5114 PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r)); 5115 for (d = 0; d < pdim; ++d) r[d] += u_t[d]; 5116 } 5117 } 5118 PetscCall(VecRestoreArray(locX_t, &x_t)); 5119 PetscCall(VecRestoreArray(locF, &fa)); 5120 } 5121 if (useFEM) { 5122 PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a)); 5123 PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec)); 5124 } 5125 if (useFVM) { 5126 PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR)); 5127 PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol)); 5128 PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL)); 5129 PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR)); 5130 if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad)); 5131 } 5132 } 5133 if (useFEM) PetscCall(ISDestroy(&chunkIS)); 5134 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 5135 5136 if (useFEM) { 5137 PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user)); 5138 5139 if (maxDegree <= 1) { 5140 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 5141 PetscCall(PetscQuadratureDestroy(&affineQuad)); 5142 } else { 5143 for (f = 0; f < Nf; ++f) { 5144 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 5145 PetscCall(PetscQuadratureDestroy(&quads[f])); 5146 } 5147 PetscCall(PetscFree2(quads, geoms)); 5148 } 5149 } 5150 5151 /* FEM */ 5152 /* 1: Get sizes from dm and dmAux */ 5153 /* 2: Get geometric data */ 5154 /* 3: Handle boundary values */ 5155 /* 4: Loop over domain */ 5156 /* Extract coefficients */ 5157 /* Loop over fields */ 5158 /* Set tiling for FE*/ 5159 /* Integrate FE residual to get elemVec */ 5160 /* Loop over subdomain */ 5161 /* Loop over quad points */ 5162 /* Transform coords to real space */ 5163 /* Evaluate field and aux fields at point */ 5164 /* Evaluate residual at point */ 5165 /* Transform residual to real space */ 5166 /* Add residual to elemVec */ 5167 /* Loop over domain */ 5168 /* Add elemVec to locX */ 5169 5170 /* FVM */ 5171 /* Get geometric data */ 5172 /* If using gradients */ 5173 /* Compute gradient data */ 5174 /* Loop over domain faces */ 5175 /* Count computational faces */ 5176 /* Reconstruct cell gradient */ 5177 /* Loop over domain cells */ 5178 /* Limit cell gradients */ 5179 /* Handle boundary values */ 5180 /* Loop over domain faces */ 5181 /* Read out field, centroid, normal, volume for each side of face */ 5182 /* Riemann solve over faces */ 5183 /* Loop over domain faces */ 5184 /* Accumulate fluxes to cells */ 5185 /* TODO Change printFEM to printDisc here */ 5186 if (mesh->printFEM) { 5187 Vec locFbc; 5188 PetscInt pStart, pEnd, p, maxDof; 5189 PetscScalar *zeroes; 5190 5191 PetscCall(VecDuplicate(locF, &locFbc)); 5192 PetscCall(VecCopy(locF, locFbc)); 5193 PetscCall(PetscSectionGetChart(section, &pStart, &pEnd)); 5194 PetscCall(PetscSectionGetMaxDof(section, &maxDof)); 5195 PetscCall(PetscCalloc1(maxDof, &zeroes)); 5196 for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES)); 5197 PetscCall(PetscFree(zeroes)); 5198 PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc)); 5199 PetscCall(VecDestroy(&locFbc)); 5200 } 5201 PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 5202 PetscFunctionReturn(PETSC_SUCCESS); 5203 } 5204 5205 /* 5206 1) Allow multiple kernels for BdResidual for hybrid DS 5207 5208 DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux 5209 5210 DONE 3) Change DMGetCellFields() to get different aux data a[] for each side 5211 - I think I just need to replace a[] with the closure from each face 5212 5213 4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before 5214 */ 5215 PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user) 5216 { 5217 DM_Plex *mesh = (DM_Plex *)dm->data; 5218 const char *name = "Hybrid Residual"; 5219 DM dmAux[3] = {NULL, NULL, NULL}; 5220 DMLabel ghostLabel = NULL; 5221 PetscDS ds = NULL; 5222 PetscDS dsIn = NULL; 5223 PetscDS dsAux[3] = {NULL, NULL, NULL}; 5224 Vec locA[3] = {NULL, NULL, NULL}; 5225 DM dmScale[3] = {NULL, NULL, NULL}; 5226 PetscDS dsScale[3] = {NULL, NULL, NULL}; 5227 Vec locS[3] = {NULL, NULL, NULL}; 5228 PetscSection section = NULL; 5229 DMField coordField = NULL; 5230 PetscScalar *a[3] = {NULL, NULL, NULL}; 5231 PetscScalar *s[3] = {NULL, NULL, NULL}; 5232 PetscScalar *u = NULL, *u_t; 5233 PetscScalar *elemVecNeg, *elemVecPos, *elemVecCoh; 5234 IS chunkIS; 5235 const PetscInt *cells; 5236 PetscInt *faces; 5237 PetscInt cStart, cEnd, numCells; 5238 PetscInt Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk; 5239 PetscInt maxDegree = PETSC_MAX_INT; 5240 PetscQuadrature affineQuad = NULL, *quads = NULL; 5241 PetscFEGeom *affineGeom = NULL, **geoms = NULL; 5242 5243 PetscFunctionBegin; 5244 if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS); 5245 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 5246 PetscCall(ISGetLocalSize(cellIS, &numCells)); 5247 if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS); 5248 if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) { 5249 const char *name; 5250 PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name)); 5251 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Form keys for each side of a cohesive surface must be different (%s, %" PetscInt_FMT ", %" PetscInt_FMT ")", name, key[0].value, key[0].part); 5252 } 5253 PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 5254 /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */ 5255 /* FEM */ 5256 /* 1: Get sizes from dm and dmAux */ 5257 PetscCall(DMGetSection(dm, §ion)); 5258 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 5259 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn)); 5260 PetscCall(PetscDSGetNumFields(ds, &Nf)); 5261 PetscCall(PetscDSGetTotalDimension(ds, &totDim)); 5262 PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn)); 5263 PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2])); 5264 if (locA[2]) { 5265 const PetscInt cellStart = cells ? cells[cStart] : cStart; 5266 5267 PetscCall(VecGetDM(locA[2], &dmAux[2])); 5268 PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL)); 5269 PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2])); 5270 { 5271 const PetscInt *cone; 5272 PetscInt c; 5273 5274 PetscCall(DMPlexGetCone(dm, cellStart, &cone)); 5275 for (c = 0; c < 2; ++c) { 5276 const PetscInt *support; 5277 PetscInt ssize, s; 5278 5279 PetscCall(DMPlexGetSupport(dm, cone[c], &support)); 5280 PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize)); 5281 PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize); 5282 if (support[0] == cellStart) s = 1; 5283 else if (support[1] == cellStart) s = 0; 5284 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart); 5285 PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c])); 5286 PetscCheck(locA[c], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Must have auxiliary vector for (%p, %" PetscInt_FMT ", %" PetscInt_FMT ")", (void *)key[c].label, key[c].value, key[c].part); 5287 if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c])); 5288 else dmAux[c] = dmAux[2]; 5289 PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL)); 5290 PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c])); 5291 } 5292 } 5293 } 5294 /* Handle mass matrix scaling 5295 The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */ 5296 PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2])); 5297 if (locS[2]) { 5298 const PetscInt cellStart = cells ? cells[cStart] : cStart; 5299 PetscInt Nb, Nbs; 5300 5301 PetscCall(VecGetDM(locS[2], &dmScale[2])); 5302 PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL)); 5303 PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2])); 5304 // BRAD: This is not set correctly 5305 key[2].field = 2; 5306 PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb)); 5307 PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs)); 5308 PetscCheck(Nb == Nbs, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Field %" PetscInt_FMT " of size %" PetscInt_FMT " cannot be scaled by field of size %" PetscInt_FMT, key[2].field, Nb, Nbs); 5309 { 5310 const PetscInt *cone; 5311 PetscInt c; 5312 5313 locS[1] = locS[0] = locS[2]; 5314 dmScale[1] = dmScale[0] = dmScale[2]; 5315 PetscCall(DMPlexGetCone(dm, cellStart, &cone)); 5316 for (c = 0; c < 2; ++c) { 5317 const PetscInt *support; 5318 PetscInt ssize, s; 5319 5320 PetscCall(DMPlexGetSupport(dm, cone[c], &support)); 5321 PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize)); 5322 PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize); 5323 if (support[0] == cellStart) s = 1; 5324 else if (support[1] == cellStart) s = 0; 5325 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart); 5326 PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL)); 5327 PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c])); 5328 } 5329 } 5330 } 5331 /* 2: Setup geometric data */ 5332 PetscCall(DMGetCoordinateField(dm, &coordField)); 5333 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 5334 if (maxDegree > 1) { 5335 PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms)); 5336 for (f = 0; f < Nf; ++f) { 5337 PetscFE fe; 5338 5339 PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe)); 5340 if (fe) { 5341 PetscCall(PetscFEGetQuadrature(fe, &quads[f])); 5342 PetscCall(PetscObjectReference((PetscObject)quads[f])); 5343 } 5344 } 5345 } 5346 /* Loop over chunks */ 5347 cellChunkSize = numCells; 5348 numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize); 5349 PetscCall(PetscCalloc1(2 * cellChunkSize, &faces)); 5350 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS)); 5351 /* Extract field coefficients */ 5352 /* NOTE This needs the end cap faces to have identical orientations */ 5353 PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2])); 5354 PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a)); 5355 PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s)); 5356 PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg)); 5357 PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos)); 5358 PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh)); 5359 for (chunk = 0; chunk < numChunks; ++chunk) { 5360 PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c; 5361 5362 PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim)); 5363 PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim)); 5364 PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim)); 5365 /* Get faces */ 5366 for (c = cS; c < cE; ++c) { 5367 const PetscInt cell = cells ? cells[c] : c; 5368 const PetscInt *cone; 5369 PetscCall(DMPlexGetCone(dm, cell, &cone)); 5370 faces[(c - cS) * 2 + 0] = cone[0]; 5371 faces[(c - cS) * 2 + 1] = cone[1]; 5372 } 5373 PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER)); 5374 /* Get geometric data */ 5375 if (maxDegree <= 1) { 5376 if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad)); 5377 if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom)); 5378 } else { 5379 for (f = 0; f < Nf; ++f) { 5380 if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f])); 5381 } 5382 } 5383 /* Loop over fields */ 5384 for (f = 0; f < Nf; ++f) { 5385 PetscFE fe; 5386 PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f]; 5387 PetscFEGeom *chunkGeom = NULL, *remGeom = NULL; 5388 PetscQuadrature quad = affineQuad ? affineQuad : quads[f]; 5389 PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb; 5390 PetscBool isCohesiveField; 5391 5392 PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe)); 5393 if (!fe) continue; 5394 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 5395 PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL)); 5396 PetscCall(PetscFEGetDimension(fe, &Nb)); 5397 blockSize = Nb; 5398 batchSize = numBlocks * blockSize; 5399 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 5400 numChunks = numCells / (numBatches * batchSize); 5401 Ne = numChunks * numBatches * batchSize; 5402 Nr = numCells % (numBatches * batchSize); 5403 offset = numCells - Nr; 5404 PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom)); 5405 PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom)); 5406 PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField)); 5407 chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE; 5408 key[0].field = f; 5409 key[1].field = f; 5410 key[2].field = f; 5411 PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVecNeg)); 5412 PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, &elemVecNeg[offset * totDim])); 5413 PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVecPos)); 5414 PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, &elemVecPos[offset * totDim])); 5415 PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVecCoh)); 5416 PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, &elemVecCoh[offset * totDim])); 5417 PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom)); 5418 PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom)); 5419 } 5420 /* Add elemVec to locX */ 5421 for (c = cS; c < cE; ++c) { 5422 const PetscInt cell = cells ? cells[c] : c; 5423 const PetscInt cind = c - cStart; 5424 PetscInt i; 5425 5426 /* Scale element values */ 5427 if (locS[0]) { 5428 PetscInt Nb, off = cind * totDim, soff = cind * totDimScale[0]; 5429 PetscBool cohesive; 5430 5431 for (f = 0; f < Nf; ++f) { 5432 PetscCall(PetscDSGetFieldSize(ds, f, &Nb)); 5433 PetscCall(PetscDSGetCohesive(ds, f, &cohesive)); 5434 if (f == key[2].field) { 5435 PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields"); 5436 // No cohesive scaling field is currently input 5437 for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i]; 5438 off += Nb; 5439 } else { 5440 const PetscInt N = cohesive ? Nb : Nb * 2; 5441 5442 for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i]; 5443 off += N; 5444 } 5445 } 5446 } else { 5447 for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i]; 5448 } 5449 if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim])); 5450 if (ghostLabel) { 5451 PetscInt ghostVal; 5452 5453 PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal)); 5454 if (ghostVal > 0) continue; 5455 } 5456 PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES)); 5457 } 5458 } 5459 PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2])); 5460 PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a)); 5461 PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s)); 5462 PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg)); 5463 PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos)); 5464 PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh)); 5465 PetscCall(PetscFree(faces)); 5466 PetscCall(ISDestroy(&chunkIS)); 5467 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 5468 if (maxDegree <= 1) { 5469 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 5470 PetscCall(PetscQuadratureDestroy(&affineQuad)); 5471 } else { 5472 for (f = 0; f < Nf; ++f) { 5473 if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 5474 if (quads) PetscCall(PetscQuadratureDestroy(&quads[f])); 5475 } 5476 PetscCall(PetscFree2(quads, geoms)); 5477 } 5478 if (mesh->printFEM) { 5479 Vec locFbc; 5480 PetscInt pStart, pEnd, p, maxDof; 5481 PetscScalar *zeroes; 5482 5483 PetscCall(VecDuplicate(locF, &locFbc)); 5484 PetscCall(VecCopy(locF, locFbc)); 5485 PetscCall(PetscSectionGetChart(section, &pStart, &pEnd)); 5486 PetscCall(PetscSectionGetMaxDof(section, &maxDof)); 5487 PetscCall(PetscCalloc1(maxDof, &zeroes)); 5488 for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES)); 5489 PetscCall(PetscFree(zeroes)); 5490 PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc)); 5491 PetscCall(VecDestroy(&locFbc)); 5492 } 5493 PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 5494 PetscFunctionReturn(PETSC_SUCCESS); 5495 } 5496 5497 static PetscErrorCode DMPlexComputeBdJacobian_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt fieldI, Vec locX, Vec locX_t, PetscReal X_tShift, Mat Jac, Mat JacP, DMField coordField, IS facetIS) 5498 { 5499 DM_Plex *mesh = (DM_Plex *)dm->data; 5500 DM plex = NULL, plexA = NULL, tdm; 5501 DMEnclosureType encAux; 5502 PetscDS ds, dsAux = NULL; 5503 PetscSection section, sectionAux = NULL; 5504 PetscSection globalSection; 5505 Vec locA = NULL, tv; 5506 PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL; 5507 PetscInt v; 5508 PetscInt Nf, totDim, totDimAux = 0; 5509 PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform; 5510 5511 PetscFunctionBegin; 5512 PetscCall(DMConvert(dm, DMPLEX, &plex)); 5513 PetscCall(DMHasBasisTransform(dm, &transform)); 5514 PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm)); 5515 PetscCall(DMGetBasisTransformVec_Internal(dm, &tv)); 5516 PetscCall(DMGetLocalSection(dm, §ion)); 5517 PetscCall(DMGetDS(dm, &ds)); 5518 PetscCall(PetscDSGetNumFields(ds, &Nf)); 5519 PetscCall(PetscDSGetTotalDimension(ds, &totDim)); 5520 PetscCall(PetscDSHasJacobian(ds, &hasJac)); 5521 PetscCall(PetscDSHasJacobianPreconditioner(ds, &hasPrec)); 5522 PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA)); 5523 if (locA) { 5524 DM dmAux; 5525 5526 PetscCall(VecGetDM(locA, &dmAux)); 5527 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 5528 PetscCall(DMConvert(dmAux, DMPLEX, &plexA)); 5529 PetscCall(DMGetDS(plexA, &dsAux)); 5530 PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux)); 5531 PetscCall(DMGetLocalSection(plexA, §ionAux)); 5532 } 5533 5534 PetscCall(DMGetGlobalSection(dm, &globalSection)); 5535 for (v = 0; v < numValues; ++v) { 5536 PetscFEGeom *fgeom; 5537 PetscInt maxDegree; 5538 PetscQuadrature qGeom = NULL; 5539 IS pointIS; 5540 const PetscInt *points; 5541 PetscFormKey key; 5542 PetscInt numFaces, face, Nq; 5543 5544 key.label = label; 5545 key.value = values[v]; 5546 key.part = 0; 5547 PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS)); 5548 if (!pointIS) continue; /* No points with that id on this process */ 5549 { 5550 IS isectIS; 5551 5552 /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */ 5553 PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS)); 5554 PetscCall(ISDestroy(&pointIS)); 5555 pointIS = isectIS; 5556 } 5557 PetscCall(ISGetLocalSize(pointIS, &numFaces)); 5558 PetscCall(ISGetIndices(pointIS, &points)); 5559 PetscCall(PetscMalloc5(numFaces * totDim, &u, locX_t ? numFaces * totDim : 0, &u_t, hasJac ? numFaces * totDim * totDim : 0, &elemMat, hasPrec ? numFaces * totDim * totDim : 0, &elemMatP, locA ? numFaces * totDimAux : 0, &a)); 5560 PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree)); 5561 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom)); 5562 if (!qGeom) { 5563 PetscFE fe; 5564 5565 PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe)); 5566 PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom)); 5567 PetscCall(PetscObjectReference((PetscObject)qGeom)); 5568 } 5569 PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL)); 5570 PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom)); 5571 for (face = 0; face < numFaces; ++face) { 5572 const PetscInt point = points[face], *support; 5573 PetscScalar *x = NULL; 5574 PetscInt i; 5575 5576 PetscCall(DMPlexGetSupport(dm, point, &support)); 5577 PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x)); 5578 for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i]; 5579 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x)); 5580 if (locX_t) { 5581 PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x)); 5582 for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i]; 5583 PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x)); 5584 } 5585 if (locA) { 5586 PetscInt subp; 5587 PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp)); 5588 PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x)); 5589 for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i]; 5590 PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x)); 5591 } 5592 } 5593 PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim)); 5594 { 5595 PetscFE fe; 5596 PetscInt Nb; 5597 /* Conforming batches */ 5598 PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize; 5599 /* Remainder */ 5600 PetscFEGeom *chunkGeom = NULL; 5601 PetscInt fieldJ, Nr, offset; 5602 5603 PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe)); 5604 PetscCall(PetscFEGetDimension(fe, &Nb)); 5605 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 5606 blockSize = Nb; 5607 batchSize = numBlocks * blockSize; 5608 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 5609 numChunks = numFaces / (numBatches * batchSize); 5610 Ne = numChunks * numBatches * batchSize; 5611 Nr = numFaces % (numBatches * batchSize); 5612 offset = numFaces - Nr; 5613 PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom)); 5614 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 5615 key.field = fieldI * Nf + fieldJ; 5616 if (hasJac) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat)); 5617 if (hasPrec) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP)); 5618 } 5619 PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom)); 5620 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 5621 key.field = fieldI * Nf + fieldJ; 5622 if (hasJac) 5623 PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMat[offset * totDim * totDim])); 5624 if (hasPrec) 5625 PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Nr, chunkGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, dsAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMatP[offset * totDim * totDim])); 5626 } 5627 PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom)); 5628 } 5629 for (face = 0; face < numFaces; ++face) { 5630 const PetscInt point = points[face], *support; 5631 5632 /* Transform to global basis before insertion in Jacobian */ 5633 PetscCall(DMPlexGetSupport(plex, point, &support)); 5634 if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim])); 5635 if (hasPrec) { 5636 if (hasJac) { 5637 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim])); 5638 PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES)); 5639 } 5640 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMatP[face * totDim * totDim])); 5641 PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMatP[face * totDim * totDim], ADD_VALUES)); 5642 } else { 5643 if (hasJac) { 5644 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim])); 5645 PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMat[face * totDim * totDim], ADD_VALUES)); 5646 } 5647 } 5648 } 5649 PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom)); 5650 PetscCall(PetscQuadratureDestroy(&qGeom)); 5651 PetscCall(ISRestoreIndices(pointIS, &points)); 5652 PetscCall(ISDestroy(&pointIS)); 5653 PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, a)); 5654 } 5655 if (plex) PetscCall(DMDestroy(&plex)); 5656 if (plexA) PetscCall(DMDestroy(&plexA)); 5657 PetscFunctionReturn(PETSC_SUCCESS); 5658 } 5659 5660 PetscErrorCode DMPlexComputeBdJacobianSingle(DM dm, PetscReal t, PetscWeakForm wf, DMLabel label, PetscInt numValues, const PetscInt values[], PetscInt field, Vec locX, Vec locX_t, PetscReal X_tShift, Mat Jac, Mat JacP) 5661 { 5662 DMField coordField; 5663 DMLabel depthLabel; 5664 IS facetIS; 5665 PetscInt dim; 5666 5667 PetscFunctionBegin; 5668 PetscCall(DMGetDimension(dm, &dim)); 5669 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 5670 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 5671 PetscCall(DMGetCoordinateField(dm, &coordField)); 5672 PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS)); 5673 PetscCall(ISDestroy(&facetIS)); 5674 PetscFunctionReturn(PETSC_SUCCESS); 5675 } 5676 5677 static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user) 5678 { 5679 PetscDS prob; 5680 PetscInt dim, numBd, bd; 5681 DMLabel depthLabel; 5682 DMField coordField = NULL; 5683 IS facetIS; 5684 5685 PetscFunctionBegin; 5686 PetscCall(DMGetDS(dm, &prob)); 5687 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 5688 PetscCall(DMGetDimension(dm, &dim)); 5689 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 5690 PetscCall(PetscDSGetNumBoundary(prob, &numBd)); 5691 PetscCall(DMGetCoordinateField(dm, &coordField)); 5692 for (bd = 0; bd < numBd; ++bd) { 5693 PetscWeakForm wf; 5694 DMBoundaryConditionType type; 5695 DMLabel label; 5696 const PetscInt *values; 5697 PetscInt fieldI, numValues; 5698 PetscObject obj; 5699 PetscClassId id; 5700 5701 PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL)); 5702 if (type & DM_BC_ESSENTIAL) continue; 5703 PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj)); 5704 PetscCall(PetscObjectGetClassId(obj, &id)); 5705 if (id != PETSCFE_CLASSID) continue; 5706 PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS)); 5707 } 5708 PetscCall(ISDestroy(&facetIS)); 5709 PetscFunctionReturn(PETSC_SUCCESS); 5710 } 5711 5712 PetscErrorCode DMPlexComputeJacobian_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *user) 5713 { 5714 DM_Plex *mesh = (DM_Plex *)dm->data; 5715 const char *name = "Jacobian"; 5716 DM dmAux = NULL, plex, tdm; 5717 DMEnclosureType encAux; 5718 Vec A, tv; 5719 DMField coordField; 5720 PetscDS prob, probAux = NULL; 5721 PetscSection section, globalSection, sectionAux; 5722 PetscScalar *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL; 5723 const PetscInt *cells; 5724 PetscInt Nf, fieldI, fieldJ; 5725 PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c; 5726 PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform; 5727 5728 PetscFunctionBegin; 5729 PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 5730 if (!cellIS) goto end; 5731 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 5732 PetscCall(ISGetLocalSize(cellIS, &numCells)); 5733 if (cStart >= cEnd) goto end; 5734 PetscCall(DMHasBasisTransform(dm, &transform)); 5735 PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm)); 5736 PetscCall(DMGetBasisTransformVec_Internal(dm, &tv)); 5737 PetscCall(DMGetLocalSection(dm, §ion)); 5738 PetscCall(DMGetGlobalSection(dm, &globalSection)); 5739 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL)); 5740 PetscCall(PetscDSGetNumFields(prob, &Nf)); 5741 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 5742 PetscCall(PetscDSHasJacobian(prob, &hasJac)); 5743 PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec)); 5744 /* user passed in the same matrix, avoid double contributions and 5745 only assemble the Jacobian */ 5746 if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE; 5747 PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn)); 5748 hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE; 5749 PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A)); 5750 if (A) { 5751 PetscCall(VecGetDM(A, &dmAux)); 5752 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 5753 PetscCall(DMConvert(dmAux, DMPLEX, &plex)); 5754 PetscCall(DMGetLocalSection(plex, §ionAux)); 5755 PetscCall(DMGetDS(dmAux, &probAux)); 5756 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 5757 } 5758 PetscCall(PetscMalloc5(numCells * totDim, &u, X_t ? numCells * totDim : 0, &u_t, hasJac ? numCells * totDim * totDim : 0, &elemMat, hasPrec ? numCells * totDim * totDim : 0, &elemMatP, hasDyn ? numCells * totDim * totDim : 0, &elemMatD)); 5759 if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a)); 5760 PetscCall(DMGetCoordinateField(dm, &coordField)); 5761 for (c = cStart; c < cEnd; ++c) { 5762 const PetscInt cell = cells ? cells[c] : c; 5763 const PetscInt cind = c - cStart; 5764 PetscScalar *x = NULL, *x_t = NULL; 5765 PetscInt i; 5766 5767 PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x)); 5768 for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i]; 5769 PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x)); 5770 if (X_t) { 5771 PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t)); 5772 for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i]; 5773 PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t)); 5774 } 5775 if (dmAux) { 5776 PetscInt subcell; 5777 PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell)); 5778 PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x)); 5779 for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i]; 5780 PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x)); 5781 } 5782 } 5783 if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim)); 5784 if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim)); 5785 if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim)); 5786 for (fieldI = 0; fieldI < Nf; ++fieldI) { 5787 PetscClassId id; 5788 PetscFE fe; 5789 PetscQuadrature qGeom = NULL; 5790 PetscInt Nb; 5791 /* Conforming batches */ 5792 PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize; 5793 /* Remainder */ 5794 PetscInt Nr, offset, Nq; 5795 PetscInt maxDegree; 5796 PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL; 5797 5798 PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe)); 5799 PetscCall(PetscObjectGetClassId((PetscObject)fe, &id)); 5800 if (id == PETSCFV_CLASSID) { 5801 hasFV = PETSC_TRUE; 5802 continue; 5803 } 5804 PetscCall(PetscFEGetDimension(fe, &Nb)); 5805 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 5806 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 5807 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom)); 5808 if (!qGeom) { 5809 PetscCall(PetscFEGetQuadrature(fe, &qGeom)); 5810 PetscCall(PetscObjectReference((PetscObject)qGeom)); 5811 } 5812 PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL)); 5813 PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 5814 blockSize = Nb; 5815 batchSize = numBlocks * blockSize; 5816 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 5817 numChunks = numCells / (numBatches * batchSize); 5818 Ne = numChunks * numBatches * batchSize; 5819 Nr = numCells % (numBatches * batchSize); 5820 offset = numCells - Nr; 5821 PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom)); 5822 PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom)); 5823 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 5824 key.field = fieldI * Nf + fieldJ; 5825 if (hasJac) { 5826 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat)); 5827 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMat[offset * totDim * totDim])); 5828 } 5829 if (hasPrec) { 5830 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP)); 5831 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMatP[offset * totDim * totDim])); 5832 } 5833 if (hasDyn) { 5834 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD)); 5835 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, a ? &a[offset * totDimAux] : NULL, t, X_tShift, &elemMatD[offset * totDim * totDim])); 5836 } 5837 } 5838 PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom)); 5839 PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom)); 5840 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 5841 PetscCall(PetscQuadratureDestroy(&qGeom)); 5842 } 5843 /* Add contribution from X_t */ 5844 if (hasDyn) { 5845 for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c]; 5846 } 5847 if (hasFV) { 5848 PetscClassId id; 5849 PetscFV fv; 5850 PetscInt offsetI, NcI, NbI = 1, fc, f; 5851 5852 for (fieldI = 0; fieldI < Nf; ++fieldI) { 5853 PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv)); 5854 PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI)); 5855 PetscCall(PetscObjectGetClassId((PetscObject)fv, &id)); 5856 if (id != PETSCFV_CLASSID) continue; 5857 /* Put in the identity */ 5858 PetscCall(PetscFVGetNumComponents(fv, &NcI)); 5859 for (c = cStart; c < cEnd; ++c) { 5860 const PetscInt cind = c - cStart; 5861 const PetscInt eOffset = cind * totDim * totDim; 5862 for (fc = 0; fc < NcI; ++fc) { 5863 for (f = 0; f < NbI; ++f) { 5864 const PetscInt i = offsetI + f * NcI + fc; 5865 if (hasPrec) { 5866 if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0; 5867 elemMatP[eOffset + i * totDim + i] = 1.0; 5868 } else { 5869 elemMat[eOffset + i * totDim + i] = 1.0; 5870 } 5871 } 5872 } 5873 } 5874 } 5875 /* No allocated space for FV stuff, so ignore the zero entries */ 5876 PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); 5877 } 5878 /* Insert values into matrix */ 5879 for (c = cStart; c < cEnd; ++c) { 5880 const PetscInt cell = cells ? cells[c] : c; 5881 const PetscInt cind = c - cStart; 5882 5883 /* Transform to global basis before insertion in Jacobian */ 5884 if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim])); 5885 if (hasPrec) { 5886 if (hasJac) { 5887 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim])); 5888 PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES)); 5889 } 5890 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim])); 5891 PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES)); 5892 } else { 5893 if (hasJac) { 5894 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim])); 5895 PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES)); 5896 } 5897 } 5898 } 5899 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 5900 if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE)); 5901 PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD)); 5902 if (dmAux) { 5903 PetscCall(PetscFree(a)); 5904 PetscCall(DMDestroy(&plex)); 5905 } 5906 /* Compute boundary integrals */ 5907 PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user)); 5908 /* Assemble matrix */ 5909 end : { 5910 PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp; 5911 5912 PetscCall(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm))); 5913 if (hasJac && hasPrec) { 5914 PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY)); 5915 PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY)); 5916 } 5917 } 5918 PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY)); 5919 PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY)); 5920 PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 5921 PetscFunctionReturn(PETSC_SUCCESS); 5922 } 5923 5924 PetscErrorCode DMPlexComputeJacobian_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal t, PetscReal X_tShift, Vec locX, Vec locX_t, Mat Jac, Mat JacP, void *user) 5925 { 5926 DM_Plex *mesh = (DM_Plex *)dm->data; 5927 const char *name = "Hybrid Jacobian"; 5928 DM dmAux[3] = {NULL, NULL, NULL}; 5929 DMLabel ghostLabel = NULL; 5930 DM plex = NULL; 5931 DM plexA = NULL; 5932 PetscDS ds = NULL; 5933 PetscDS dsIn = NULL; 5934 PetscDS dsAux[3] = {NULL, NULL, NULL}; 5935 Vec locA[3] = {NULL, NULL, NULL}; 5936 DM dmScale[3] = {NULL, NULL, NULL}; 5937 PetscDS dsScale[3] = {NULL, NULL, NULL}; 5938 Vec locS[3] = {NULL, NULL, NULL}; 5939 PetscSection section = NULL; 5940 PetscSection sectionAux[3] = {NULL, NULL, NULL}; 5941 DMField coordField = NULL; 5942 PetscScalar *a[3] = {NULL, NULL, NULL}; 5943 PetscScalar *s[3] = {NULL, NULL, NULL}; 5944 PetscScalar *u = NULL, *u_t; 5945 PetscScalar *elemMatNeg, *elemMatPos, *elemMatCoh; 5946 PetscScalar *elemMatNegP, *elemMatPosP, *elemMatCohP; 5947 PetscSection globalSection; 5948 IS chunkIS; 5949 const PetscInt *cells; 5950 PetscInt *faces; 5951 PetscInt cStart, cEnd, numCells; 5952 PetscInt Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk; 5953 PetscInt maxDegree = PETSC_MAX_INT; 5954 PetscQuadrature affineQuad = NULL, *quads = NULL; 5955 PetscFEGeom *affineGeom = NULL, **geoms = NULL; 5956 PetscBool hasBdJac, hasBdPrec; 5957 5958 PetscFunctionBegin; 5959 if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS); 5960 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 5961 PetscCall(ISGetLocalSize(cellIS, &numCells)); 5962 if (cStart >= cEnd) PetscFunctionReturn(PETSC_SUCCESS); 5963 if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) { 5964 const char *name; 5965 PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name)); 5966 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Form keys for each side of a cohesive surface must be different (%s, %" PetscInt_FMT ", %" PetscInt_FMT ")", name, key[0].value, key[0].part); 5967 } 5968 PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 5969 PetscCall(DMConvert(dm, DMPLEX, &plex)); 5970 PetscCall(DMGetSection(dm, §ion)); 5971 PetscCall(DMGetGlobalSection(dm, &globalSection)); 5972 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 5973 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn)); 5974 PetscCall(PetscDSGetNumFields(ds, &Nf)); 5975 PetscCall(PetscDSGetTotalDimension(ds, &totDim)); 5976 PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn)); 5977 PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac)); 5978 PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec)); 5979 PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2])); 5980 if (locA[2]) { 5981 const PetscInt cellStart = cells ? cells[cStart] : cStart; 5982 5983 PetscCall(VecGetDM(locA[2], &dmAux[2])); 5984 PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA)); 5985 PetscCall(DMGetSection(dmAux[2], §ionAux[2])); 5986 PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL)); 5987 PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2])); 5988 { 5989 const PetscInt *cone; 5990 PetscInt c; 5991 5992 PetscCall(DMPlexGetCone(dm, cellStart, &cone)); 5993 for (c = 0; c < 2; ++c) { 5994 const PetscInt *support; 5995 PetscInt ssize, s; 5996 5997 PetscCall(DMPlexGetSupport(dm, cone[c], &support)); 5998 PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize)); 5999 PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize); 6000 if (support[0] == cellStart) s = 1; 6001 else if (support[1] == cellStart) s = 0; 6002 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart); 6003 PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c])); 6004 if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c])); 6005 else dmAux[c] = dmAux[2]; 6006 PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL)); 6007 PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c])); 6008 } 6009 } 6010 } 6011 /* Handle mass matrix scaling 6012 The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */ 6013 PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2])); 6014 if (locS[2]) { 6015 const PetscInt cellStart = cells ? cells[cStart] : cStart; 6016 PetscInt Nb, Nbs; 6017 6018 PetscCall(VecGetDM(locS[2], &dmScale[2])); 6019 PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL)); 6020 PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2])); 6021 // BRAD: This is not set correctly 6022 key[2].field = 2; 6023 PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb)); 6024 PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs)); 6025 PetscCheck(Nb == Nbs, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Field %" PetscInt_FMT " of size %" PetscInt_FMT " cannot be scaled by field of size %" PetscInt_FMT, key[2].field, Nb, Nbs); 6026 { 6027 const PetscInt *cone; 6028 PetscInt c; 6029 6030 locS[1] = locS[0] = locS[2]; 6031 dmScale[1] = dmScale[0] = dmScale[2]; 6032 PetscCall(DMPlexGetCone(dm, cellStart, &cone)); 6033 for (c = 0; c < 2; ++c) { 6034 const PetscInt *support; 6035 PetscInt ssize, s; 6036 6037 PetscCall(DMPlexGetSupport(dm, cone[c], &support)); 6038 PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize)); 6039 PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", cone[c], cellStart, ssize); 6040 if (support[0] == cellStart) s = 1; 6041 else if (support[1] == cellStart) s = 0; 6042 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart); 6043 PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL)); 6044 PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c])); 6045 } 6046 } 6047 } 6048 /* 2: Setup geometric data */ 6049 PetscCall(DMGetCoordinateField(dm, &coordField)); 6050 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 6051 if (maxDegree > 1) { 6052 PetscInt f; 6053 PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms)); 6054 for (f = 0; f < Nf; ++f) { 6055 PetscFE fe; 6056 6057 PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe)); 6058 if (fe) { 6059 PetscCall(PetscFEGetQuadrature(fe, &quads[f])); 6060 PetscCall(PetscObjectReference((PetscObject)quads[f])); 6061 } 6062 } 6063 } 6064 /* Loop over chunks */ 6065 cellChunkSize = numCells; 6066 numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize); 6067 PetscCall(PetscCalloc1(2 * cellChunkSize, &faces)); 6068 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS)); 6069 /* Extract field coefficients */ 6070 /* NOTE This needs the end cap faces to have identical orientations */ 6071 PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2])); 6072 PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a)); 6073 PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s)); 6074 PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg)); 6075 PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos)); 6076 PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh)); 6077 PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP)); 6078 PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP)); 6079 PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP)); 6080 for (chunk = 0; chunk < numChunks; ++chunk) { 6081 PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c; 6082 6083 if (hasBdJac) { 6084 PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim)); 6085 PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim)); 6086 PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim)); 6087 } 6088 if (hasBdPrec) { 6089 PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim)); 6090 PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim)); 6091 PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim)); 6092 } 6093 /* Get faces */ 6094 for (c = cS; c < cE; ++c) { 6095 const PetscInt cell = cells ? cells[c] : c; 6096 const PetscInt *cone; 6097 PetscCall(DMPlexGetCone(plex, cell, &cone)); 6098 faces[(c - cS) * 2 + 0] = cone[0]; 6099 faces[(c - cS) * 2 + 1] = cone[1]; 6100 } 6101 PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER)); 6102 if (maxDegree <= 1) { 6103 if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad)); 6104 if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom)); 6105 } else { 6106 PetscInt f; 6107 for (f = 0; f < Nf; ++f) { 6108 if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f])); 6109 } 6110 } 6111 6112 for (fieldI = 0; fieldI < Nf; ++fieldI) { 6113 PetscFE feI; 6114 PetscFEGeom *geom = affineGeom ? affineGeom : geoms[fieldI]; 6115 PetscFEGeom *chunkGeom = NULL, *remGeom = NULL; 6116 PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI]; 6117 PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb; 6118 PetscBool isCohesiveField; 6119 6120 PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI)); 6121 if (!feI) continue; 6122 PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches)); 6123 PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL)); 6124 PetscCall(PetscFEGetDimension(feI, &Nb)); 6125 blockSize = Nb; 6126 batchSize = numBlocks * blockSize; 6127 PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches)); 6128 numChunks = numCells / (numBatches * batchSize); 6129 Ne = numChunks * numBatches * batchSize; 6130 Nr = numCells % (numBatches * batchSize); 6131 offset = numCells - Nr; 6132 PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom)); 6133 PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom)); 6134 PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField)); 6135 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 6136 PetscFE feJ; 6137 6138 PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ)); 6139 if (!feJ) continue; 6140 key[0].field = fieldI * Nf + fieldJ; 6141 key[1].field = fieldI * Nf + fieldJ; 6142 key[2].field = fieldI * Nf + fieldJ; 6143 if (hasBdJac) { 6144 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg)); 6145 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNeg[offset * totDim * totDim])); 6146 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos)); 6147 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPos[offset * totDim * totDim])); 6148 } 6149 if (hasBdPrec) { 6150 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP)); 6151 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[0], &a[0][offset * totDimAux[0]], t, X_tShift, &elemMatNegP[offset * totDim * totDim])); 6152 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP)); 6153 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[1], &a[1][offset * totDimAux[1]], t, X_tShift, &elemMatPosP[offset * totDim * totDim])); 6154 } 6155 if (hasBdJac) { 6156 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh)); 6157 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCoh[offset * totDim * totDim])); 6158 } 6159 if (hasBdPrec) { 6160 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP)); 6161 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Nr, remGeom, &u[offset * totDimIn], u_t ? &u_t[offset * totDimIn] : NULL, dsAux[2], &a[2][offset * totDimAux[2]], t, X_tShift, &elemMatCohP[offset * totDim * totDim])); 6162 } 6163 } 6164 PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom)); 6165 PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom)); 6166 } 6167 /* Insert values into matrix */ 6168 for (c = cS; c < cE; ++c) { 6169 const PetscInt cell = cells ? cells[c] : c; 6170 const PetscInt cind = c - cS, coff = cind * totDim * totDim; 6171 PetscInt i, j; 6172 6173 /* Scale element values */ 6174 if (locS[0]) { 6175 PetscInt Nb, soff = cind * totDimScale[0], off = 0; 6176 PetscBool cohesive; 6177 6178 for (fieldI = 0; fieldI < Nf; ++fieldI) { 6179 PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb)); 6180 PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive)); 6181 6182 if (fieldI == key[2].field) { 6183 PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields"); 6184 for (i = 0; i < Nb; ++i) { 6185 for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNeg[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPos[coff + (off + i) * totDim + j]; 6186 if (hasBdPrec) 6187 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += s[0][soff + i] * elemMatNegP[coff + (off + i) * totDim + j] + s[1][soff + i] * elemMatPosP[coff + (off + i) * totDim + j]; 6188 } 6189 off += Nb; 6190 } else { 6191 const PetscInt N = cohesive ? Nb : Nb * 2; 6192 6193 for (i = 0; i < N; ++i) { 6194 for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j]; 6195 if (hasBdPrec) 6196 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j]; 6197 } 6198 off += N; 6199 } 6200 } 6201 } else { 6202 for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i]; 6203 if (hasBdPrec) 6204 for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i]; 6205 } 6206 if (hasBdPrec) { 6207 if (hasBdJac) { 6208 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim])); 6209 PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES)); 6210 } 6211 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim])); 6212 PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES)); 6213 } else if (hasBdJac) { 6214 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim])); 6215 PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES)); 6216 } 6217 } 6218 } 6219 PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2])); 6220 PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a)); 6221 PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg)); 6222 PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos)); 6223 PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh)); 6224 PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP)); 6225 PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP)); 6226 PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP)); 6227 PetscCall(PetscFree(faces)); 6228 PetscCall(ISDestroy(&chunkIS)); 6229 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 6230 if (maxDegree <= 1) { 6231 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 6232 PetscCall(PetscQuadratureDestroy(&affineQuad)); 6233 } else { 6234 PetscInt f; 6235 for (f = 0; f < Nf; ++f) { 6236 if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 6237 if (quads) PetscCall(PetscQuadratureDestroy(&quads[f])); 6238 } 6239 PetscCall(PetscFree2(quads, geoms)); 6240 } 6241 if (dmAux[2]) PetscCall(DMDestroy(&plexA)); 6242 PetscCall(DMDestroy(&plex)); 6243 PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 6244 PetscFunctionReturn(PETSC_SUCCESS); 6245 } 6246 6247 /* 6248 DMPlexComputeJacobian_Action_Internal - Form the local portion of the Jacobian action Z = J(X) Y at the local solution X using pointwise functions specified by the user. 6249 6250 Input Parameters: 6251 + dm - The mesh 6252 . key - The PetscWeakFormKey indicating where integration should happen 6253 . cellIS - The cells to integrate over 6254 . t - The time 6255 . X_tShift - The multiplier for the Jacobian with respect to X_t 6256 . X - Local solution vector 6257 . X_t - Time-derivative of the local solution vector 6258 . Y - Local input vector 6259 - user - the user context 6260 6261 Output Parameter: 6262 . Z - Local output vector 6263 6264 Note: 6265 We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator, 6266 like a GPU, or vectorize on a multicore machine. 6267 */ 6268 PetscErrorCode DMPlexComputeJacobian_Action_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Vec Y, Vec Z, void *user) 6269 { 6270 DM_Plex *mesh = (DM_Plex *)dm->data; 6271 const char *name = "Jacobian"; 6272 DM dmAux = NULL, plex, plexAux = NULL; 6273 DMEnclosureType encAux; 6274 Vec A; 6275 DMField coordField; 6276 PetscDS prob, probAux = NULL; 6277 PetscQuadrature quad; 6278 PetscSection section, globalSection, sectionAux; 6279 PetscScalar *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z; 6280 const PetscInt *cells; 6281 PetscInt Nf, fieldI, fieldJ; 6282 PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c; 6283 PetscBool hasDyn; 6284 6285 PetscFunctionBegin; 6286 if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS); 6287 PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 6288 PetscCall(DMConvert(dm, DMPLEX, &plex)); 6289 PetscCall(ISGetLocalSize(cellIS, &numCells)); 6290 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 6291 PetscCall(DMGetLocalSection(dm, §ion)); 6292 PetscCall(DMGetGlobalSection(dm, &globalSection)); 6293 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL)); 6294 PetscCall(PetscDSGetNumFields(prob, &Nf)); 6295 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 6296 PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn)); 6297 hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE; 6298 PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A)); 6299 if (A) { 6300 PetscCall(VecGetDM(A, &dmAux)); 6301 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 6302 PetscCall(DMConvert(dmAux, DMPLEX, &plexAux)); 6303 PetscCall(DMGetLocalSection(plexAux, §ionAux)); 6304 PetscCall(DMGetDS(dmAux, &probAux)); 6305 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 6306 } 6307 PetscCall(VecSet(Z, 0.0)); 6308 PetscCall(PetscMalloc6(numCells * totDim, &u, X_t ? numCells * totDim : 0, &u_t, numCells * totDim * totDim, &elemMat, hasDyn ? numCells * totDim * totDim : 0, &elemMatD, numCells * totDim, &y, totDim, &z)); 6309 if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a)); 6310 PetscCall(DMGetCoordinateField(dm, &coordField)); 6311 for (c = cStart; c < cEnd; ++c) { 6312 const PetscInt cell = cells ? cells[c] : c; 6313 const PetscInt cind = c - cStart; 6314 PetscScalar *x = NULL, *x_t = NULL; 6315 PetscInt i; 6316 6317 PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x)); 6318 for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i]; 6319 PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x)); 6320 if (X_t) { 6321 PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t)); 6322 for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i]; 6323 PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t)); 6324 } 6325 if (dmAux) { 6326 PetscInt subcell; 6327 PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell)); 6328 PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x)); 6329 for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i]; 6330 PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x)); 6331 } 6332 PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x)); 6333 for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i]; 6334 PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x)); 6335 } 6336 PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim)); 6337 if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim)); 6338 for (fieldI = 0; fieldI < Nf; ++fieldI) { 6339 PetscFE fe; 6340 PetscInt Nb; 6341 /* Conforming batches */ 6342 PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize; 6343 /* Remainder */ 6344 PetscInt Nr, offset, Nq; 6345 PetscQuadrature qGeom = NULL; 6346 PetscInt maxDegree; 6347 PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL; 6348 6349 PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe)); 6350 PetscCall(PetscFEGetQuadrature(fe, &quad)); 6351 PetscCall(PetscFEGetDimension(fe, &Nb)); 6352 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 6353 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 6354 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom)); 6355 if (!qGeom) { 6356 PetscCall(PetscFEGetQuadrature(fe, &qGeom)); 6357 PetscCall(PetscObjectReference((PetscObject)qGeom)); 6358 } 6359 PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL)); 6360 PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 6361 blockSize = Nb; 6362 batchSize = numBlocks * blockSize; 6363 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 6364 numChunks = numCells / (numBatches * batchSize); 6365 Ne = numChunks * numBatches * batchSize; 6366 Nr = numCells % (numBatches * batchSize); 6367 offset = numCells - Nr; 6368 PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom)); 6369 PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom)); 6370 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 6371 key.field = fieldI * Nf + fieldJ; 6372 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat)); 6373 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMat[offset * totDim * totDim])); 6374 if (hasDyn) { 6375 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD)); 6376 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Nr, remGeom, &u[offset * totDim], u_t ? &u_t[offset * totDim] : NULL, probAux, &a[offset * totDimAux], t, X_tShift, &elemMatD[offset * totDim * totDim])); 6377 } 6378 } 6379 PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom)); 6380 PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom)); 6381 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 6382 PetscCall(PetscQuadratureDestroy(&qGeom)); 6383 } 6384 if (hasDyn) { 6385 for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c]; 6386 } 6387 for (c = cStart; c < cEnd; ++c) { 6388 const PetscInt cell = cells ? cells[c] : c; 6389 const PetscInt cind = c - cStart; 6390 const PetscBLASInt M = totDim, one = 1; 6391 const PetscScalar a = 1.0, b = 0.0; 6392 6393 PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one)); 6394 if (mesh->printFEM > 1) { 6395 PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim])); 6396 PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim])); 6397 PetscCall(DMPrintCellVector(c, "Z", totDim, z)); 6398 } 6399 PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES)); 6400 } 6401 PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z)); 6402 if (mesh->printFEM) { 6403 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n")); 6404 PetscCall(VecView(Z, NULL)); 6405 } 6406 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 6407 PetscCall(PetscFree(a)); 6408 PetscCall(DMDestroy(&plexAux)); 6409 PetscCall(DMDestroy(&plex)); 6410 PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 6411 PetscFunctionReturn(PETSC_SUCCESS); 6412 } 6413