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