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