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 PetscSimplePointFn *func = (PetscSimplePointFn *)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 PetscSimplePointFn *func_t = (PetscSimplePointFn *)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 2324 PetscCall(PetscDSGetObjective(prob, f, &obj_func)); 2325 PetscCall(PetscDSGetFieldOffset(prob, f, &foff)); 2326 if (obj_func) { 2327 for (c = 0; c < numCells; ++c) { 2328 PetscScalar *u_x; 2329 PetscScalar lint = 0.; 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 (**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[]), 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 (face = 0; face < numFaces; ++face) { 2547 const PetscInt point = points[face], *support; 2548 PetscScalar *x = NULL; 2549 2550 PetscCall(DMPlexGetSupport(dm, point, &support)); 2551 PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x)); 2552 for (PetscInt i = 0; i < totDim; ++i) u[face * totDim + i] = x[i]; 2553 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x)); 2554 if (locA) { 2555 PetscInt subp; 2556 PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp)); 2557 PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x)); 2558 for (PetscInt i = 0; i < totDimAux; ++i) a[f * totDimAux + i] = x[i]; 2559 PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x)); 2560 } 2561 } 2562 for (field = 0; field < Nf; ++field) { 2563 PetscFE fe; 2564 2565 PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fe)); 2566 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom)); 2567 if (!qGeom) { 2568 PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom)); 2569 PetscCall(PetscObjectReference((PetscObject)qGeom)); 2570 } 2571 PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL)); 2572 PetscCall(DMPlexGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom)); 2573 /* Get blocking */ 2574 { 2575 PetscQuadrature q; 2576 PetscInt numBatches, batchSize, numBlocks, blockSize; 2577 PetscInt Nq, Nb; 2578 2579 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 2580 PetscCall(PetscFEGetQuadrature(fe, &q)); 2581 PetscCall(PetscQuadratureGetData(q, NULL, NULL, &Nq, NULL, NULL)); 2582 PetscCall(PetscFEGetDimension(fe, &Nb)); 2583 blockSize = Nb * Nq; 2584 batchSize = numBlocks * blockSize; 2585 chunkSize = numBatches * batchSize; 2586 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 2587 numChunks = numFaces / chunkSize; 2588 Nr = numFaces % chunkSize; 2589 offset = numFaces - Nr; 2590 } 2591 /* Do integration for each field */ 2592 for (chunk = 0; chunk < numChunks; ++chunk) { 2593 PetscCall(PetscFEGeomGetChunk(fgeom, chunk * chunkSize, (chunk + 1) * chunkSize, &chunkGeom)); 2594 PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], chunkSize, chunkGeom, &u[chunk * chunkSize * totDim], probAux, PetscSafePointerPlusOffset(a, chunk * chunkSize * totDimAux), &fintegral[chunk * chunkSize * Nf])); 2595 PetscCall(PetscFEGeomRestoreChunk(fgeom, 0, offset, &chunkGeom)); 2596 } 2597 PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom)); 2598 PetscCall(PetscFEIntegrateBd(prob, field, funcs[field], Nr, chunkGeom, &u[offset * totDim], probAux, PetscSafePointerPlusOffset(a, offset * totDimAux), &fintegral[offset * Nf])); 2599 PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom)); 2600 /* Cleanup data arrays */ 2601 PetscCall(DMPlexRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom)); 2602 PetscCall(PetscQuadratureDestroy(&qGeom)); 2603 } 2604 PetscCall(PetscFree2(u, a)); 2605 PetscCall(ISRestoreIndices(pointIS, &points)); 2606 } 2607 if (plex) PetscCall(DMDestroy(&plex)); 2608 if (plexA) PetscCall(DMDestroy(&plexA)); 2609 PetscFunctionReturn(PETSC_SUCCESS); 2610 } 2611 2612 /*@C 2613 DMPlexComputeBdIntegral - Form the integral over the specified boundary from the global input X using pointwise functions specified by the user 2614 2615 Input Parameters: 2616 + dm - The mesh 2617 . X - Global input vector 2618 . label - The boundary `DMLabel` 2619 . numVals - The number of label values to use, or `PETSC_DETERMINE` for all values 2620 . vals - The label values to use, or NULL for all values 2621 . funcs - The functions to integrate along the boundary for each field 2622 - user - The user context 2623 2624 Output Parameter: 2625 . integral - Integral for each field 2626 2627 Level: developer 2628 2629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeIntegralFEM()`, `DMPlexComputeBdResidualFEM()` 2630 @*/ 2631 PetscErrorCode DMPlexComputeBdIntegral(DM dm, Vec X, DMLabel label, PetscInt numVals, const PetscInt vals[], void (**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[]), PetscScalar *integral, void *user) 2632 { 2633 Vec locX; 2634 PetscSection section; 2635 DMLabel depthLabel; 2636 IS facetIS; 2637 PetscInt dim, Nf, f, v; 2638 2639 PetscFunctionBegin; 2640 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 2641 PetscValidHeaderSpecific(X, VEC_CLASSID, 2); 2642 PetscValidHeaderSpecific(label, DMLABEL_CLASSID, 3); 2643 if (vals) PetscAssertPointer(vals, 5); 2644 PetscAssertPointer(integral, 7); 2645 PetscCall(PetscLogEventBegin(DMPLEX_IntegralFEM, dm, 0, 0, 0)); 2646 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 2647 PetscCall(DMGetDimension(dm, &dim)); 2648 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 2649 PetscCall(DMGetLocalSection(dm, §ion)); 2650 PetscCall(PetscSectionGetNumFields(section, &Nf)); 2651 /* Get local solution with boundary values */ 2652 PetscCall(DMGetLocalVector(dm, &locX)); 2653 PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locX, 0.0, NULL, NULL, NULL)); 2654 PetscCall(DMGlobalToLocalBegin(dm, X, INSERT_VALUES, locX)); 2655 PetscCall(DMGlobalToLocalEnd(dm, X, INSERT_VALUES, locX)); 2656 /* Loop over label values */ 2657 PetscCall(PetscArrayzero(integral, Nf)); 2658 for (v = 0; v < numVals; ++v) { 2659 IS pointIS; 2660 PetscInt numFaces, face; 2661 PetscScalar *fintegral; 2662 2663 PetscCall(DMLabelGetStratumIS(label, vals[v], &pointIS)); 2664 if (!pointIS) continue; /* No points with that id on this process */ 2665 { 2666 IS isectIS; 2667 2668 /* TODO: Special cases of ISIntersect where it is quick to check a priori if one is a superset of the other */ 2669 PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS)); 2670 PetscCall(ISDestroy(&pointIS)); 2671 pointIS = isectIS; 2672 } 2673 PetscCall(ISGetLocalSize(pointIS, &numFaces)); 2674 PetscCall(PetscCalloc1(numFaces * Nf, &fintegral)); 2675 PetscCall(DMPlexComputeBdIntegral_Internal(dm, locX, pointIS, funcs, fintegral, user)); 2676 /* Sum point contributions into integral */ 2677 for (f = 0; f < Nf; ++f) 2678 for (face = 0; face < numFaces; ++face) integral[f] += fintegral[face * Nf + f]; 2679 PetscCall(PetscFree(fintegral)); 2680 PetscCall(ISDestroy(&pointIS)); 2681 } 2682 PetscCall(DMRestoreLocalVector(dm, &locX)); 2683 PetscCall(ISDestroy(&facetIS)); 2684 PetscCall(PetscLogEventEnd(DMPLEX_IntegralFEM, dm, 0, 0, 0)); 2685 PetscFunctionReturn(PETSC_SUCCESS); 2686 } 2687 2688 /*@ 2689 DMPlexComputeInterpolatorNested - Form the local portion of the interpolation matrix from the coarse `DM` to a uniformly refined `DM`. 2690 2691 Input Parameters: 2692 + dmc - The coarse mesh 2693 . dmf - The fine mesh 2694 . isRefined - Flag indicating regular refinement, rather than the same topology 2695 - user - The user context 2696 2697 Output Parameter: 2698 . In - The interpolation matrix 2699 2700 Level: developer 2701 2702 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()` 2703 @*/ 2704 PetscErrorCode DMPlexComputeInterpolatorNested(DM dmc, DM dmf, PetscBool isRefined, Mat In, void *user) 2705 { 2706 DM_Plex *mesh = (DM_Plex *)dmc->data; 2707 const char *name = "Interpolator"; 2708 PetscFE *feRef; 2709 PetscFV *fvRef; 2710 PetscSection fsection, fglobalSection; 2711 PetscSection csection, cglobalSection; 2712 PetscScalar *elemMat; 2713 PetscInt dim, Nf, f, fieldI, fieldJ, offsetI, offsetJ, cStart, cEnd, c; 2714 PetscInt cTotDim = 0, rTotDim = 0; 2715 2716 PetscFunctionBegin; 2717 PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0)); 2718 PetscCall(DMGetDimension(dmf, &dim)); 2719 PetscCall(DMGetLocalSection(dmf, &fsection)); 2720 PetscCall(DMGetGlobalSection(dmf, &fglobalSection)); 2721 PetscCall(DMGetLocalSection(dmc, &csection)); 2722 PetscCall(DMGetGlobalSection(dmc, &cglobalSection)); 2723 PetscCall(PetscSectionGetNumFields(fsection, &Nf)); 2724 PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd)); 2725 PetscCall(PetscCalloc2(Nf, &feRef, Nf, &fvRef)); 2726 for (f = 0; f < Nf; ++f) { 2727 PetscObject obj, objc; 2728 PetscClassId id, idc; 2729 PetscInt rNb = 0, Nc = 0, cNb = 0; 2730 2731 PetscCall(DMGetField(dmf, f, NULL, &obj)); 2732 PetscCall(PetscObjectGetClassId(obj, &id)); 2733 if (id == PETSCFE_CLASSID) { 2734 PetscFE fe = (PetscFE)obj; 2735 2736 if (isRefined) { 2737 PetscCall(PetscFERefine(fe, &feRef[f])); 2738 } else { 2739 PetscCall(PetscObjectReference((PetscObject)fe)); 2740 feRef[f] = fe; 2741 } 2742 PetscCall(PetscFEGetDimension(feRef[f], &rNb)); 2743 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 2744 } else if (id == PETSCFV_CLASSID) { 2745 PetscFV fv = (PetscFV)obj; 2746 PetscDualSpace Q; 2747 2748 if (isRefined) { 2749 PetscCall(PetscFVRefine(fv, &fvRef[f])); 2750 } else { 2751 PetscCall(PetscObjectReference((PetscObject)fv)); 2752 fvRef[f] = fv; 2753 } 2754 PetscCall(PetscFVGetDualSpace(fvRef[f], &Q)); 2755 PetscCall(PetscDualSpaceGetDimension(Q, &rNb)); 2756 PetscCall(PetscFVGetDualSpace(fv, &Q)); 2757 PetscCall(PetscFVGetNumComponents(fv, &Nc)); 2758 } 2759 PetscCall(DMGetField(dmc, f, NULL, &objc)); 2760 PetscCall(PetscObjectGetClassId(objc, &idc)); 2761 if (idc == PETSCFE_CLASSID) { 2762 PetscFE fe = (PetscFE)objc; 2763 2764 PetscCall(PetscFEGetDimension(fe, &cNb)); 2765 } else if (id == PETSCFV_CLASSID) { 2766 PetscFV fv = (PetscFV)obj; 2767 PetscDualSpace Q; 2768 2769 PetscCall(PetscFVGetDualSpace(fv, &Q)); 2770 PetscCall(PetscDualSpaceGetDimension(Q, &cNb)); 2771 } 2772 rTotDim += rNb; 2773 cTotDim += cNb; 2774 } 2775 PetscCall(PetscMalloc1(rTotDim * cTotDim, &elemMat)); 2776 PetscCall(PetscArrayzero(elemMat, rTotDim * cTotDim)); 2777 for (fieldI = 0, offsetI = 0; fieldI < Nf; ++fieldI) { 2778 PetscDualSpace Qref; 2779 PetscQuadrature f; 2780 const PetscReal *qpoints, *qweights; 2781 PetscReal *points; 2782 PetscInt npoints = 0, Nc, Np, fpdim, i, k, p, d; 2783 2784 /* Compose points from all dual basis functionals */ 2785 if (feRef[fieldI]) { 2786 PetscCall(PetscFEGetDualSpace(feRef[fieldI], &Qref)); 2787 PetscCall(PetscFEGetNumComponents(feRef[fieldI], &Nc)); 2788 } else { 2789 PetscCall(PetscFVGetDualSpace(fvRef[fieldI], &Qref)); 2790 PetscCall(PetscFVGetNumComponents(fvRef[fieldI], &Nc)); 2791 } 2792 PetscCall(PetscDualSpaceGetDimension(Qref, &fpdim)); 2793 for (i = 0; i < fpdim; ++i) { 2794 PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f)); 2795 PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, NULL, NULL)); 2796 npoints += Np; 2797 } 2798 PetscCall(PetscMalloc1(npoints * dim, &points)); 2799 for (i = 0, k = 0; i < fpdim; ++i) { 2800 PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f)); 2801 PetscCall(PetscQuadratureGetData(f, NULL, NULL, &Np, &qpoints, NULL)); 2802 for (p = 0; p < Np; ++p, ++k) 2803 for (d = 0; d < dim; ++d) points[k * dim + d] = qpoints[p * dim + d]; 2804 } 2805 2806 for (fieldJ = 0, offsetJ = 0; fieldJ < Nf; ++fieldJ) { 2807 PetscObject obj; 2808 PetscClassId id; 2809 PetscInt NcJ = 0, cpdim = 0, j, qNc; 2810 2811 PetscCall(DMGetField(dmc, fieldJ, NULL, &obj)); 2812 PetscCall(PetscObjectGetClassId(obj, &id)); 2813 if (id == PETSCFE_CLASSID) { 2814 PetscFE fe = (PetscFE)obj; 2815 PetscTabulation T = NULL; 2816 2817 /* Evaluate basis at points */ 2818 PetscCall(PetscFEGetNumComponents(fe, &NcJ)); 2819 PetscCall(PetscFEGetDimension(fe, &cpdim)); 2820 /* For now, fields only interpolate themselves */ 2821 if (fieldI == fieldJ) { 2822 PetscCheck(Nc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, Nc, NcJ); 2823 PetscCall(PetscFECreateTabulation(fe, 1, npoints, points, 0, &T)); 2824 for (i = 0, k = 0; i < fpdim; ++i) { 2825 PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f)); 2826 PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights)); 2827 PetscCheck(qNc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, NcJ); 2828 for (p = 0; p < Np; ++p, ++k) { 2829 for (j = 0; j < cpdim; ++j) { 2830 /* 2831 cTotDim: Total columns in element interpolation matrix, sum of number of dual basis functionals in each field 2832 offsetI, offsetJ: Offsets into the larger element interpolation matrix for different fields 2833 fpdim, i, cpdim, j: Dofs for fine and coarse grids, correspond to dual space basis functionals 2834 qNC, Nc, Ncj, c: Number of components in this field 2835 Np, p: Number of quad points in the fine grid functional i 2836 k: i*Np + p, overall point number for the interpolation 2837 */ 2838 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += T->T[0][k * cpdim * NcJ + j * Nc + c] * qweights[p * qNc + c]; 2839 } 2840 } 2841 } 2842 PetscCall(PetscTabulationDestroy(&T)); 2843 } 2844 } else if (id == PETSCFV_CLASSID) { 2845 PetscFV fv = (PetscFV)obj; 2846 2847 /* Evaluate constant function at points */ 2848 PetscCall(PetscFVGetNumComponents(fv, &NcJ)); 2849 cpdim = 1; 2850 /* For now, fields only interpolate themselves */ 2851 if (fieldI == fieldJ) { 2852 PetscCheck(Nc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, Nc, NcJ); 2853 for (i = 0, k = 0; i < fpdim; ++i) { 2854 PetscCall(PetscDualSpaceGetFunctional(Qref, i, &f)); 2855 PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, NULL, &qweights)); 2856 PetscCheck(qNc == NcJ, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, NcJ); 2857 for (p = 0; p < Np; ++p, ++k) { 2858 for (j = 0; j < cpdim; ++j) { 2859 for (c = 0; c < Nc; ++c) elemMat[(offsetI + i) * cTotDim + offsetJ + j] += 1.0 * qweights[p * qNc + c]; 2860 } 2861 } 2862 } 2863 } 2864 } 2865 offsetJ += cpdim; 2866 } 2867 offsetI += fpdim; 2868 PetscCall(PetscFree(points)); 2869 } 2870 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(0, name, rTotDim, cTotDim, elemMat)); 2871 /* Preallocate matrix */ 2872 { 2873 Mat preallocator; 2874 PetscScalar *vals; 2875 PetscInt *cellCIndices, *cellFIndices; 2876 PetscInt locRows, locCols, cell; 2877 2878 PetscCall(MatGetLocalSize(In, &locRows, &locCols)); 2879 PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &preallocator)); 2880 PetscCall(MatSetType(preallocator, MATPREALLOCATOR)); 2881 PetscCall(MatSetSizes(preallocator, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE)); 2882 PetscCall(MatSetUp(preallocator)); 2883 PetscCall(PetscCalloc3(rTotDim * cTotDim, &vals, cTotDim, &cellCIndices, rTotDim, &cellFIndices)); 2884 for (cell = cStart; cell < cEnd; ++cell) { 2885 if (isRefined) { 2886 PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, cell, cellCIndices, cellFIndices)); 2887 PetscCall(MatSetValues(preallocator, rTotDim, cellFIndices, cTotDim, cellCIndices, vals, INSERT_VALUES)); 2888 } else { 2889 PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, preallocator, cell, vals, INSERT_VALUES)); 2890 } 2891 } 2892 PetscCall(PetscFree3(vals, cellCIndices, cellFIndices)); 2893 PetscCall(MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY)); 2894 PetscCall(MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY)); 2895 PetscCall(MatPreallocatorPreallocate(preallocator, PETSC_TRUE, In)); 2896 PetscCall(MatDestroy(&preallocator)); 2897 } 2898 /* Fill matrix */ 2899 PetscCall(MatZeroEntries(In)); 2900 for (c = cStart; c < cEnd; ++c) { 2901 if (isRefined) { 2902 PetscCall(DMPlexMatSetClosureRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, In, c, elemMat, INSERT_VALUES)); 2903 } else { 2904 PetscCall(DMPlexMatSetClosureGeneral(dmf, fsection, fglobalSection, PETSC_FALSE, dmc, csection, cglobalSection, PETSC_FALSE, In, c, elemMat, INSERT_VALUES)); 2905 } 2906 } 2907 for (f = 0; f < Nf; ++f) PetscCall(PetscFEDestroy(&feRef[f])); 2908 PetscCall(PetscFree2(feRef, fvRef)); 2909 PetscCall(PetscFree(elemMat)); 2910 PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY)); 2911 PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY)); 2912 if (mesh->printFEM > 1) { 2913 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)In), "%s:\n", name)); 2914 PetscCall(MatFilter(In, 1.0e-10, PETSC_FALSE, PETSC_FALSE)); 2915 PetscCall(MatView(In, NULL)); 2916 } 2917 PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0)); 2918 PetscFunctionReturn(PETSC_SUCCESS); 2919 } 2920 2921 PetscErrorCode DMPlexComputeMassMatrixNested(DM dmc, DM dmf, Mat mass, void *user) 2922 { 2923 SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_SUP, "Laziness"); 2924 } 2925 2926 /*@ 2927 DMPlexComputeInterpolatorGeneral - Form the local portion of the interpolation matrix from the coarse `DM` to a non-nested fine `DM`. 2928 2929 Input Parameters: 2930 + dmf - The fine mesh 2931 . dmc - The coarse mesh 2932 - user - The user context 2933 2934 Output Parameter: 2935 . In - The interpolation matrix 2936 2937 Level: developer 2938 2939 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()` 2940 @*/ 2941 PetscErrorCode DMPlexComputeInterpolatorGeneral(DM dmc, DM dmf, Mat In, void *user) 2942 { 2943 DM_Plex *mesh = (DM_Plex *)dmf->data; 2944 const char *name = "Interpolator"; 2945 PetscDS prob; 2946 Mat interp; 2947 PetscSection fsection, globalFSection; 2948 PetscSection csection, globalCSection; 2949 PetscInt locRows, locCols; 2950 PetscReal *x, *v0, *J, *invJ, detJ; 2951 PetscReal *v0c, *Jc, *invJc, detJc; 2952 PetscScalar *elemMat; 2953 PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell, s; 2954 2955 PetscFunctionBegin; 2956 PetscCall(PetscLogEventBegin(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0)); 2957 PetscCall(DMGetCoordinateDim(dmc, &dim)); 2958 PetscCall(DMGetDS(dmc, &prob)); 2959 PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL)); 2960 PetscCall(PetscDSGetNumFields(prob, &Nf)); 2961 PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ)); 2962 PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc)); 2963 PetscCall(DMGetLocalSection(dmf, &fsection)); 2964 PetscCall(DMGetGlobalSection(dmf, &globalFSection)); 2965 PetscCall(DMGetLocalSection(dmc, &csection)); 2966 PetscCall(DMGetGlobalSection(dmc, &globalCSection)); 2967 PetscCall(DMPlexGetSimplexOrBoxCells(dmf, 0, &cStart, &cEnd)); 2968 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 2969 PetscCall(PetscMalloc1(totDim, &elemMat)); 2970 2971 PetscCall(MatGetLocalSize(In, &locRows, &locCols)); 2972 PetscCall(MatCreate(PetscObjectComm((PetscObject)In), &interp)); 2973 PetscCall(MatSetType(interp, MATPREALLOCATOR)); 2974 PetscCall(MatSetSizes(interp, locRows, locCols, PETSC_DETERMINE, PETSC_DETERMINE)); 2975 PetscCall(MatSetUp(interp)); 2976 for (s = 0; s < 2; ++s) { 2977 for (field = 0; field < Nf; ++field) { 2978 PetscObject obj; 2979 PetscClassId id; 2980 PetscDualSpace Q = NULL; 2981 PetscTabulation T = NULL; 2982 PetscQuadrature f; 2983 const PetscReal *qpoints, *qweights; 2984 PetscInt Nc, qNc, Np, fpdim, off, i, d; 2985 2986 PetscCall(PetscDSGetFieldOffset(prob, field, &off)); 2987 PetscCall(PetscDSGetDiscretization(prob, field, &obj)); 2988 PetscCall(PetscObjectGetClassId(obj, &id)); 2989 if (id == PETSCFE_CLASSID) { 2990 PetscFE fe = (PetscFE)obj; 2991 2992 PetscCall(PetscFEGetDualSpace(fe, &Q)); 2993 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 2994 if (s) PetscCall(PetscFECreateTabulation(fe, 1, 1, x, 0, &T)); 2995 } else if (id == PETSCFV_CLASSID) { 2996 PetscFV fv = (PetscFV)obj; 2997 2998 PetscCall(PetscFVGetDualSpace(fv, &Q)); 2999 Nc = 1; 3000 } else SETERRQ(PetscObjectComm((PetscObject)dmc), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, field); 3001 PetscCall(PetscDualSpaceGetDimension(Q, &fpdim)); 3002 /* For each fine grid cell */ 3003 for (cell = cStart; cell < cEnd; ++cell) { 3004 PetscInt *findices, *cindices; 3005 PetscInt numFIndices, numCIndices; 3006 3007 PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3008 PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ)); 3009 PetscCheck(numFIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of fine indices %" PetscInt_FMT " != %" PetscInt_FMT " dual basis vecs", numFIndices, totDim); 3010 for (i = 0; i < fpdim; ++i) { 3011 Vec pointVec; 3012 PetscScalar *pV; 3013 PetscSF coarseCellSF = NULL; 3014 const PetscSFNode *coarseCells; 3015 PetscInt numCoarseCells, cpdim, row = findices[i + off], q, c, j; 3016 3017 /* Get points from the dual basis functional quadrature */ 3018 PetscCall(PetscDualSpaceGetFunctional(Q, i, &f)); 3019 PetscCall(PetscQuadratureGetData(f, NULL, &qNc, &Np, &qpoints, &qweights)); 3020 PetscCheck(qNc == Nc, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in quadrature %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, qNc, Nc); 3021 PetscCall(VecCreateSeq(PETSC_COMM_SELF, Np * dim, &pointVec)); 3022 PetscCall(VecSetBlockSize(pointVec, dim)); 3023 PetscCall(VecGetArray(pointVec, &pV)); 3024 for (q = 0; q < Np; ++q) { 3025 const PetscReal xi0[3] = {-1., -1., -1.}; 3026 3027 /* Transform point to real space */ 3028 CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x); 3029 for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d]; 3030 } 3031 PetscCall(VecRestoreArray(pointVec, &pV)); 3032 /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */ 3033 /* OPT: Read this out from preallocation information */ 3034 PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF)); 3035 /* Update preallocation info */ 3036 PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells)); 3037 PetscCheck(numCoarseCells == Np, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located"); 3038 PetscCall(VecGetArray(pointVec, &pV)); 3039 for (ccell = 0; ccell < numCoarseCells; ++ccell) { 3040 PetscReal pVReal[3]; 3041 const PetscReal xi0[3] = {-1., -1., -1.}; 3042 3043 PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3044 if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetDimension((PetscFE)obj, &cpdim)); 3045 else cpdim = 1; 3046 3047 if (s) { 3048 /* Transform points from real space to coarse reference space */ 3049 PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc)); 3050 for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]); 3051 CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x); 3052 3053 if (id == PETSCFE_CLASSID) { 3054 /* Evaluate coarse basis on contained point */ 3055 PetscCall(PetscFEComputeTabulation((PetscFE)obj, 1, x, 0, T)); 3056 PetscCall(PetscArrayzero(elemMat, cpdim)); 3057 /* Get elemMat entries by multiplying by weight */ 3058 for (j = 0; j < cpdim; ++j) { 3059 for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * qweights[ccell * qNc + c]; 3060 } 3061 } else { 3062 for (j = 0; j < cpdim; ++j) { 3063 for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * qweights[ccell * qNc + c]; 3064 } 3065 } 3066 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat)); 3067 } 3068 /* Update interpolator */ 3069 PetscCheck(numCIndices == totDim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, totDim); 3070 PetscCall(MatSetValues(interp, 1, &row, cpdim, &cindices[off], elemMat, INSERT_VALUES)); 3071 PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3072 } 3073 PetscCall(VecRestoreArray(pointVec, &pV)); 3074 PetscCall(PetscSFDestroy(&coarseCellSF)); 3075 PetscCall(VecDestroy(&pointVec)); 3076 } 3077 PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3078 } 3079 if (s && id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T)); 3080 } 3081 if (!s) { 3082 PetscCall(MatAssemblyBegin(interp, MAT_FINAL_ASSEMBLY)); 3083 PetscCall(MatAssemblyEnd(interp, MAT_FINAL_ASSEMBLY)); 3084 PetscCall(MatPreallocatorPreallocate(interp, PETSC_TRUE, In)); 3085 PetscCall(MatDestroy(&interp)); 3086 interp = In; 3087 } 3088 } 3089 PetscCall(PetscFree3(v0, J, invJ)); 3090 PetscCall(PetscFree3(v0c, Jc, invJc)); 3091 PetscCall(PetscFree(elemMat)); 3092 PetscCall(MatAssemblyBegin(In, MAT_FINAL_ASSEMBLY)); 3093 PetscCall(MatAssemblyEnd(In, MAT_FINAL_ASSEMBLY)); 3094 PetscCall(PetscLogEventEnd(DMPLEX_InterpolatorFEM, dmc, dmf, 0, 0)); 3095 PetscFunctionReturn(PETSC_SUCCESS); 3096 } 3097 3098 /*@ 3099 DMPlexComputeMassMatrixGeneral - Form the local portion of the mass matrix from the coarse `DM` to a non-nested fine `DM`. 3100 3101 Input Parameters: 3102 + dmf - The fine mesh 3103 . dmc - The coarse mesh 3104 - user - The user context 3105 3106 Output Parameter: 3107 . mass - The mass matrix 3108 3109 Level: developer 3110 3111 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeMassMatrixNested()`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeInterpolatorGeneral()`, `DMPlexComputeJacobianFEM()` 3112 @*/ 3113 PetscErrorCode DMPlexComputeMassMatrixGeneral(DM dmc, DM dmf, Mat mass, void *user) 3114 { 3115 DM_Plex *mesh = (DM_Plex *)dmf->data; 3116 const char *name = "Mass Matrix"; 3117 PetscDS prob; 3118 PetscSection fsection, csection, globalFSection, globalCSection; 3119 PetscHSetIJ ht; 3120 PetscLayout rLayout; 3121 PetscInt *dnz, *onz; 3122 PetscInt locRows, rStart, rEnd; 3123 PetscReal *x, *v0, *J, *invJ, detJ; 3124 PetscReal *v0c, *Jc, *invJc, detJc; 3125 PetscScalar *elemMat; 3126 PetscInt dim, Nf, field, totDim, cStart, cEnd, cell, ccell; 3127 3128 PetscFunctionBegin; 3129 PetscCall(DMGetCoordinateDim(dmc, &dim)); 3130 PetscCall(DMGetDS(dmc, &prob)); 3131 PetscCall(PetscDSGetWorkspace(prob, &x, NULL, NULL, NULL, NULL)); 3132 PetscCall(PetscDSGetNumFields(prob, &Nf)); 3133 PetscCall(PetscMalloc3(dim, &v0, dim * dim, &J, dim * dim, &invJ)); 3134 PetscCall(PetscMalloc3(dim, &v0c, dim * dim, &Jc, dim * dim, &invJc)); 3135 PetscCall(DMGetLocalSection(dmf, &fsection)); 3136 PetscCall(DMGetGlobalSection(dmf, &globalFSection)); 3137 PetscCall(DMGetLocalSection(dmc, &csection)); 3138 PetscCall(DMGetGlobalSection(dmc, &globalCSection)); 3139 PetscCall(DMPlexGetHeightStratum(dmf, 0, &cStart, &cEnd)); 3140 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 3141 PetscCall(PetscMalloc1(totDim, &elemMat)); 3142 3143 PetscCall(MatGetLocalSize(mass, &locRows, NULL)); 3144 PetscCall(PetscLayoutCreate(PetscObjectComm((PetscObject)mass), &rLayout)); 3145 PetscCall(PetscLayoutSetLocalSize(rLayout, locRows)); 3146 PetscCall(PetscLayoutSetBlockSize(rLayout, 1)); 3147 PetscCall(PetscLayoutSetUp(rLayout)); 3148 PetscCall(PetscLayoutGetRange(rLayout, &rStart, &rEnd)); 3149 PetscCall(PetscLayoutDestroy(&rLayout)); 3150 PetscCall(PetscCalloc2(locRows, &dnz, locRows, &onz)); 3151 PetscCall(PetscHSetIJCreate(&ht)); 3152 for (field = 0; field < Nf; ++field) { 3153 PetscObject obj; 3154 PetscClassId id; 3155 PetscQuadrature quad; 3156 const PetscReal *qpoints; 3157 PetscInt Nq, Nc, i, d; 3158 3159 PetscCall(PetscDSGetDiscretization(prob, field, &obj)); 3160 PetscCall(PetscObjectGetClassId(obj, &id)); 3161 if (id == PETSCFE_CLASSID) PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad)); 3162 else PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad)); 3163 PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, NULL)); 3164 /* For each fine grid cell */ 3165 for (cell = cStart; cell < cEnd; ++cell) { 3166 Vec pointVec; 3167 PetscScalar *pV; 3168 PetscSF coarseCellSF = NULL; 3169 const PetscSFNode *coarseCells; 3170 PetscInt numCoarseCells, q, c; 3171 PetscInt *findices, *cindices; 3172 PetscInt numFIndices, numCIndices; 3173 3174 PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3175 PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ)); 3176 /* Get points from the quadrature */ 3177 PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec)); 3178 PetscCall(VecSetBlockSize(pointVec, dim)); 3179 PetscCall(VecGetArray(pointVec, &pV)); 3180 for (q = 0; q < Nq; ++q) { 3181 const PetscReal xi0[3] = {-1., -1., -1.}; 3182 3183 /* Transform point to real space */ 3184 CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x); 3185 for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d]; 3186 } 3187 PetscCall(VecRestoreArray(pointVec, &pV)); 3188 /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */ 3189 PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF)); 3190 PetscCall(PetscSFViewFromOptions(coarseCellSF, NULL, "-interp_sf_view")); 3191 /* Update preallocation info */ 3192 PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells)); 3193 PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located"); 3194 { 3195 PetscHashIJKey key; 3196 PetscBool missing; 3197 3198 for (i = 0; i < numFIndices; ++i) { 3199 key.i = findices[i]; 3200 if (key.i >= 0) { 3201 /* Get indices for coarse elements */ 3202 for (ccell = 0; ccell < numCoarseCells; ++ccell) { 3203 PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3204 for (c = 0; c < numCIndices; ++c) { 3205 key.j = cindices[c]; 3206 if (key.j < 0) continue; 3207 PetscCall(PetscHSetIJQueryAdd(ht, key, &missing)); 3208 if (missing) { 3209 if ((key.j >= rStart) && (key.j < rEnd)) ++dnz[key.i - rStart]; 3210 else ++onz[key.i - rStart]; 3211 } 3212 } 3213 PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3214 } 3215 } 3216 } 3217 } 3218 PetscCall(PetscSFDestroy(&coarseCellSF)); 3219 PetscCall(VecDestroy(&pointVec)); 3220 PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3221 } 3222 } 3223 PetscCall(PetscHSetIJDestroy(&ht)); 3224 PetscCall(MatXAIJSetPreallocation(mass, 1, dnz, onz, NULL, NULL)); 3225 PetscCall(MatSetOption(mass, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE)); 3226 PetscCall(PetscFree2(dnz, onz)); 3227 for (field = 0; field < Nf; ++field) { 3228 PetscObject obj; 3229 PetscClassId id; 3230 PetscTabulation T, Tfine; 3231 PetscQuadrature quad; 3232 const PetscReal *qpoints, *qweights; 3233 PetscInt Nq, Nc, i, d; 3234 3235 PetscCall(PetscDSGetDiscretization(prob, field, &obj)); 3236 PetscCall(PetscObjectGetClassId(obj, &id)); 3237 if (id == PETSCFE_CLASSID) { 3238 PetscCall(PetscFEGetQuadrature((PetscFE)obj, &quad)); 3239 PetscCall(PetscFEGetCellTabulation((PetscFE)obj, 1, &Tfine)); 3240 PetscCall(PetscFECreateTabulation((PetscFE)obj, 1, 1, x, 0, &T)); 3241 } else { 3242 PetscCall(PetscFVGetQuadrature((PetscFV)obj, &quad)); 3243 } 3244 PetscCall(PetscQuadratureGetData(quad, NULL, &Nc, &Nq, &qpoints, &qweights)); 3245 /* For each fine grid cell */ 3246 for (cell = cStart; cell < cEnd; ++cell) { 3247 Vec pointVec; 3248 PetscScalar *pV; 3249 PetscSF coarseCellSF = NULL; 3250 const PetscSFNode *coarseCells; 3251 PetscInt numCoarseCells, cpdim, q, c, j; 3252 PetscInt *findices, *cindices; 3253 PetscInt numFIndices, numCIndices; 3254 3255 PetscCall(DMPlexGetClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3256 PetscCall(DMPlexComputeCellGeometryFEM(dmf, cell, NULL, v0, J, invJ, &detJ)); 3257 /* Get points from the quadrature */ 3258 PetscCall(VecCreateSeq(PETSC_COMM_SELF, Nq * dim, &pointVec)); 3259 PetscCall(VecSetBlockSize(pointVec, dim)); 3260 PetscCall(VecGetArray(pointVec, &pV)); 3261 for (q = 0; q < Nq; ++q) { 3262 const PetscReal xi0[3] = {-1., -1., -1.}; 3263 3264 /* Transform point to real space */ 3265 CoordinatesRefToReal(dim, dim, xi0, v0, J, &qpoints[q * dim], x); 3266 for (d = 0; d < dim; ++d) pV[q * dim + d] = x[d]; 3267 } 3268 PetscCall(VecRestoreArray(pointVec, &pV)); 3269 /* Get set of coarse cells that overlap points (would like to group points by coarse cell) */ 3270 PetscCall(DMLocatePoints(dmc, pointVec, DM_POINTLOCATION_NEAREST, &coarseCellSF)); 3271 /* Update matrix */ 3272 PetscCall(PetscSFGetGraph(coarseCellSF, NULL, &numCoarseCells, NULL, &coarseCells)); 3273 PetscCheck(numCoarseCells == Nq, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Not all closure points located"); 3274 PetscCall(VecGetArray(pointVec, &pV)); 3275 for (ccell = 0; ccell < numCoarseCells; ++ccell) { 3276 PetscReal pVReal[3]; 3277 const PetscReal xi0[3] = {-1., -1., -1.}; 3278 3279 PetscCall(DMPlexGetClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3280 /* Transform points from real space to coarse reference space */ 3281 PetscCall(DMPlexComputeCellGeometryFEM(dmc, coarseCells[ccell].index, NULL, v0c, Jc, invJc, &detJc)); 3282 for (d = 0; d < dim; ++d) pVReal[d] = PetscRealPart(pV[ccell * dim + d]); 3283 CoordinatesRealToRef(dim, dim, xi0, v0c, invJc, pVReal, x); 3284 3285 if (id == PETSCFE_CLASSID) { 3286 PetscFE fe = (PetscFE)obj; 3287 3288 /* Evaluate coarse basis on contained point */ 3289 PetscCall(PetscFEGetDimension(fe, &cpdim)); 3290 PetscCall(PetscFEComputeTabulation(fe, 1, x, 0, T)); 3291 /* Get elemMat entries by multiplying by weight */ 3292 for (i = 0; i < numFIndices; ++i) { 3293 PetscCall(PetscArrayzero(elemMat, cpdim)); 3294 for (j = 0; j < cpdim; ++j) { 3295 for (c = 0; c < Nc; ++c) elemMat[j] += T->T[0][j * Nc + c] * Tfine->T[0][(ccell * numFIndices + i) * Nc + c] * qweights[ccell * Nc + c] * detJ; 3296 } 3297 /* Update interpolator */ 3298 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat)); 3299 PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim); 3300 PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES)); 3301 } 3302 } else { 3303 cpdim = 1; 3304 for (i = 0; i < numFIndices; ++i) { 3305 PetscCall(PetscArrayzero(elemMat, cpdim)); 3306 for (j = 0; j < cpdim; ++j) { 3307 for (c = 0; c < Nc; ++c) elemMat[j] += 1.0 * 1.0 * qweights[ccell * Nc + c] * detJ; 3308 } 3309 /* Update interpolator */ 3310 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, 1, numCIndices, elemMat)); 3311 PetscCall(PetscPrintf(PETSC_COMM_SELF, "Nq: %" PetscInt_FMT " %" PetscInt_FMT " Nf: %" PetscInt_FMT " %" PetscInt_FMT " Nc: %" PetscInt_FMT " %" PetscInt_FMT "\n", ccell, Nq, i, numFIndices, j, numCIndices)); 3312 PetscCheck(numCIndices == cpdim, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Number of element matrix columns %" PetscInt_FMT " != %" PetscInt_FMT, numCIndices, cpdim); 3313 PetscCall(MatSetValues(mass, 1, &findices[i], numCIndices, cindices, elemMat, ADD_VALUES)); 3314 } 3315 } 3316 PetscCall(DMPlexRestoreClosureIndices(dmc, csection, globalCSection, coarseCells[ccell].index, PETSC_FALSE, &numCIndices, &cindices, NULL, NULL)); 3317 } 3318 PetscCall(VecRestoreArray(pointVec, &pV)); 3319 PetscCall(PetscSFDestroy(&coarseCellSF)); 3320 PetscCall(VecDestroy(&pointVec)); 3321 PetscCall(DMPlexRestoreClosureIndices(dmf, fsection, globalFSection, cell, PETSC_FALSE, &numFIndices, &findices, NULL, NULL)); 3322 } 3323 if (id == PETSCFE_CLASSID) PetscCall(PetscTabulationDestroy(&T)); 3324 } 3325 PetscCall(PetscFree3(v0, J, invJ)); 3326 PetscCall(PetscFree3(v0c, Jc, invJc)); 3327 PetscCall(PetscFree(elemMat)); 3328 PetscCall(MatAssemblyBegin(mass, MAT_FINAL_ASSEMBLY)); 3329 PetscCall(MatAssemblyEnd(mass, MAT_FINAL_ASSEMBLY)); 3330 PetscFunctionReturn(PETSC_SUCCESS); 3331 } 3332 3333 /*@ 3334 DMPlexComputeInjectorFEM - Compute a mapping from coarse unknowns to fine unknowns 3335 3336 Input Parameters: 3337 + dmc - The coarse mesh 3338 . dmf - The fine mesh 3339 - user - The user context 3340 3341 Output Parameter: 3342 . sc - The mapping 3343 3344 Level: developer 3345 3346 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexComputeInterpolatorNested()`, `DMPlexComputeJacobianFEM()` 3347 @*/ 3348 PetscErrorCode DMPlexComputeInjectorFEM(DM dmc, DM dmf, VecScatter *sc, void *user) 3349 { 3350 PetscDS prob; 3351 PetscFE *feRef; 3352 PetscFV *fvRef; 3353 Vec fv, cv; 3354 IS fis, cis; 3355 PetscSection fsection, fglobalSection, csection, cglobalSection; 3356 PetscInt *cmap, *cellCIndices, *cellFIndices, *cindices, *findices; 3357 PetscInt cTotDim, fTotDim = 0, Nf, f, field, cStart, cEnd, c, dim, d, startC, endC, offsetC, offsetF, m; 3358 PetscBool *needAvg; 3359 3360 PetscFunctionBegin; 3361 PetscCall(PetscLogEventBegin(DMPLEX_InjectorFEM, dmc, dmf, 0, 0)); 3362 PetscCall(DMGetDimension(dmf, &dim)); 3363 PetscCall(DMGetLocalSection(dmf, &fsection)); 3364 PetscCall(DMGetGlobalSection(dmf, &fglobalSection)); 3365 PetscCall(DMGetLocalSection(dmc, &csection)); 3366 PetscCall(DMGetGlobalSection(dmc, &cglobalSection)); 3367 PetscCall(PetscSectionGetNumFields(fsection, &Nf)); 3368 PetscCall(DMPlexGetSimplexOrBoxCells(dmc, 0, &cStart, &cEnd)); 3369 PetscCall(DMGetDS(dmc, &prob)); 3370 PetscCall(PetscCalloc3(Nf, &feRef, Nf, &fvRef, Nf, &needAvg)); 3371 for (f = 0; f < Nf; ++f) { 3372 PetscObject obj; 3373 PetscClassId id; 3374 PetscInt fNb = 0, Nc = 0; 3375 3376 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 3377 PetscCall(PetscObjectGetClassId(obj, &id)); 3378 if (id == PETSCFE_CLASSID) { 3379 PetscFE fe = (PetscFE)obj; 3380 PetscSpace sp; 3381 PetscInt maxDegree; 3382 3383 PetscCall(PetscFERefine(fe, &feRef[f])); 3384 PetscCall(PetscFEGetDimension(feRef[f], &fNb)); 3385 PetscCall(PetscFEGetNumComponents(fe, &Nc)); 3386 PetscCall(PetscFEGetBasisSpace(fe, &sp)); 3387 PetscCall(PetscSpaceGetDegree(sp, NULL, &maxDegree)); 3388 if (!maxDegree) needAvg[f] = PETSC_TRUE; 3389 } else if (id == PETSCFV_CLASSID) { 3390 PetscFV fv = (PetscFV)obj; 3391 PetscDualSpace Q; 3392 3393 PetscCall(PetscFVRefine(fv, &fvRef[f])); 3394 PetscCall(PetscFVGetDualSpace(fvRef[f], &Q)); 3395 PetscCall(PetscDualSpaceGetDimension(Q, &fNb)); 3396 PetscCall(PetscFVGetNumComponents(fv, &Nc)); 3397 needAvg[f] = PETSC_TRUE; 3398 } 3399 fTotDim += fNb; 3400 } 3401 PetscCall(PetscDSGetTotalDimension(prob, &cTotDim)); 3402 PetscCall(PetscMalloc1(cTotDim, &cmap)); 3403 for (field = 0, offsetC = 0, offsetF = 0; field < Nf; ++field) { 3404 PetscFE feC; 3405 PetscFV fvC; 3406 PetscDualSpace QF, QC; 3407 PetscInt order = -1, NcF, NcC, fpdim, cpdim; 3408 3409 if (feRef[field]) { 3410 PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&feC)); 3411 PetscCall(PetscFEGetNumComponents(feC, &NcC)); 3412 PetscCall(PetscFEGetNumComponents(feRef[field], &NcF)); 3413 PetscCall(PetscFEGetDualSpace(feRef[field], &QF)); 3414 PetscCall(PetscDualSpaceGetOrder(QF, &order)); 3415 PetscCall(PetscDualSpaceGetDimension(QF, &fpdim)); 3416 PetscCall(PetscFEGetDualSpace(feC, &QC)); 3417 PetscCall(PetscDualSpaceGetDimension(QC, &cpdim)); 3418 } else { 3419 PetscCall(PetscDSGetDiscretization(prob, field, (PetscObject *)&fvC)); 3420 PetscCall(PetscFVGetNumComponents(fvC, &NcC)); 3421 PetscCall(PetscFVGetNumComponents(fvRef[field], &NcF)); 3422 PetscCall(PetscFVGetDualSpace(fvRef[field], &QF)); 3423 PetscCall(PetscDualSpaceGetDimension(QF, &fpdim)); 3424 PetscCall(PetscFVGetDualSpace(fvC, &QC)); 3425 PetscCall(PetscDualSpaceGetDimension(QC, &cpdim)); 3426 } 3427 PetscCheck(NcF == NcC, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of components in fine space field %" PetscInt_FMT " does not match coarse field %" PetscInt_FMT, NcF, NcC); 3428 for (c = 0; c < cpdim; ++c) { 3429 PetscQuadrature cfunc; 3430 const PetscReal *cqpoints, *cqweights; 3431 PetscInt NqcC, NpC; 3432 PetscBool found = PETSC_FALSE; 3433 3434 PetscCall(PetscDualSpaceGetFunctional(QC, c, &cfunc)); 3435 PetscCall(PetscQuadratureGetData(cfunc, NULL, &NqcC, &NpC, &cqpoints, &cqweights)); 3436 PetscCheck(NqcC == NcC, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %" PetscInt_FMT " must match number of field components %" PetscInt_FMT, NqcC, NcC); 3437 PetscCheck(NpC == 1 || !feRef[field], PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Do not know how to do injection for moments"); 3438 for (f = 0; f < fpdim; ++f) { 3439 PetscQuadrature ffunc; 3440 const PetscReal *fqpoints, *fqweights; 3441 PetscReal sum = 0.0; 3442 PetscInt NqcF, NpF; 3443 3444 PetscCall(PetscDualSpaceGetFunctional(QF, f, &ffunc)); 3445 PetscCall(PetscQuadratureGetData(ffunc, NULL, &NqcF, &NpF, &fqpoints, &fqweights)); 3446 PetscCheck(NqcF == NcF, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of quadrature components %" PetscInt_FMT " must match number of field components %" PetscInt_FMT, NqcF, NcF); 3447 if (NpC != NpF) continue; 3448 for (d = 0; d < dim; ++d) sum += PetscAbsReal(cqpoints[d] - fqpoints[d]); 3449 if (sum > 1.0e-9) continue; 3450 for (d = 0; d < NcC; ++d) sum += PetscAbsReal(cqweights[d] * fqweights[d]); 3451 if (sum < 1.0e-9) continue; 3452 cmap[offsetC + c] = offsetF + f; 3453 found = PETSC_TRUE; 3454 break; 3455 } 3456 if (!found) { 3457 /* TODO We really want the average here, but some asshole put VecScatter in the interface */ 3458 if (fvRef[field] || (feRef[field] && order == 0)) { 3459 cmap[offsetC + c] = offsetF + 0; 3460 } else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Could not locate matching functional for injection"); 3461 } 3462 } 3463 offsetC += cpdim; 3464 offsetF += fpdim; 3465 } 3466 for (f = 0; f < Nf; ++f) { 3467 PetscCall(PetscFEDestroy(&feRef[f])); 3468 PetscCall(PetscFVDestroy(&fvRef[f])); 3469 } 3470 PetscCall(PetscFree3(feRef, fvRef, needAvg)); 3471 3472 PetscCall(DMGetGlobalVector(dmf, &fv)); 3473 PetscCall(DMGetGlobalVector(dmc, &cv)); 3474 PetscCall(VecGetOwnershipRange(cv, &startC, &endC)); 3475 PetscCall(PetscSectionGetConstrainedStorageSize(cglobalSection, &m)); 3476 PetscCall(PetscMalloc2(cTotDim, &cellCIndices, fTotDim, &cellFIndices)); 3477 PetscCall(PetscMalloc1(m, &cindices)); 3478 PetscCall(PetscMalloc1(m, &findices)); 3479 for (d = 0; d < m; ++d) cindices[d] = findices[d] = -1; 3480 for (c = cStart; c < cEnd; ++c) { 3481 PetscCall(DMPlexMatGetClosureIndicesRefined(dmf, fsection, fglobalSection, dmc, csection, cglobalSection, c, cellCIndices, cellFIndices)); 3482 for (d = 0; d < cTotDim; ++d) { 3483 if ((cellCIndices[d] < startC) || (cellCIndices[d] >= endC)) continue; 3484 PetscCheck(!(findices[cellCIndices[d] - startC] >= 0) || !(findices[cellCIndices[d] - startC] != cellFIndices[cmap[d]]), PETSC_COMM_SELF, PETSC_ERR_PLIB, "Cell %" PetscInt_FMT " Coarse dof %" PetscInt_FMT " maps to both %" PetscInt_FMT " and %" PetscInt_FMT, c, cindices[cellCIndices[d] - startC], findices[cellCIndices[d] - startC], cellFIndices[cmap[d]]); 3485 cindices[cellCIndices[d] - startC] = cellCIndices[d]; 3486 findices[cellCIndices[d] - startC] = cellFIndices[cmap[d]]; 3487 } 3488 } 3489 PetscCall(PetscFree(cmap)); 3490 PetscCall(PetscFree2(cellCIndices, cellFIndices)); 3491 3492 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, cindices, PETSC_OWN_POINTER, &cis)); 3493 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, m, findices, PETSC_OWN_POINTER, &fis)); 3494 PetscCall(VecScatterCreate(cv, cis, fv, fis, sc)); 3495 PetscCall(ISDestroy(&cis)); 3496 PetscCall(ISDestroy(&fis)); 3497 PetscCall(DMRestoreGlobalVector(dmf, &fv)); 3498 PetscCall(DMRestoreGlobalVector(dmc, &cv)); 3499 PetscCall(PetscLogEventEnd(DMPLEX_InjectorFEM, dmc, dmf, 0, 0)); 3500 PetscFunctionReturn(PETSC_SUCCESS); 3501 } 3502 3503 /*@C 3504 DMPlexGetCellFields - Retrieve the field values values for a chunk of cells 3505 3506 Input Parameters: 3507 + dm - The `DM` 3508 . cellIS - The cells to include 3509 . locX - A local vector with the solution fields 3510 . locX_t - A local vector with solution field time derivatives, or NULL 3511 - locA - A local vector with auxiliary fields, or NULL 3512 3513 Output Parameters: 3514 + u - The field coefficients 3515 . u_t - The fields derivative coefficients 3516 - a - The auxiliary field coefficients 3517 3518 Level: developer 3519 3520 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()` 3521 @*/ 3522 PetscErrorCode DMPlexGetCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a) 3523 { 3524 DM plex, plexA = NULL; 3525 DMEnclosureType encAux; 3526 PetscSection section, sectionAux; 3527 PetscDS prob; 3528 const PetscInt *cells; 3529 PetscInt cStart, cEnd, numCells, totDim, totDimAux, c; 3530 3531 PetscFunctionBegin; 3532 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3533 PetscValidHeaderSpecific(locX, VEC_CLASSID, 3); 3534 if (locX_t) PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 4); 3535 if (locA) PetscValidHeaderSpecific(locA, VEC_CLASSID, 5); 3536 PetscAssertPointer(u, 6); 3537 PetscAssertPointer(u_t, 7); 3538 PetscAssertPointer(a, 8); 3539 PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE)); 3540 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 3541 PetscCall(DMGetLocalSection(dm, §ion)); 3542 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL)); 3543 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 3544 if (locA) { 3545 DM dmAux; 3546 PetscDS probAux; 3547 3548 PetscCall(VecGetDM(locA, &dmAux)); 3549 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 3550 PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE)); 3551 PetscCall(DMGetLocalSection(dmAux, §ionAux)); 3552 PetscCall(DMGetDS(dmAux, &probAux)); 3553 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 3554 } 3555 numCells = cEnd - cStart; 3556 PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u)); 3557 if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t)); 3558 else *u_t = NULL; 3559 if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a)); 3560 else *a = NULL; 3561 for (c = cStart; c < cEnd; ++c) { 3562 const PetscInt cell = cells ? cells[c] : c; 3563 const PetscInt cind = c - cStart; 3564 PetscScalar *x = NULL, *x_t = NULL, *ul = *u, *ul_t = *u_t, *al = *a; 3565 PetscInt i; 3566 3567 PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, NULL, &x)); 3568 for (i = 0; i < totDim; ++i) ul[cind * totDim + i] = x[i]; 3569 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, NULL, &x)); 3570 if (locX_t) { 3571 PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &x_t)); 3572 for (i = 0; i < totDim; ++i) ul_t[cind * totDim + i] = x_t[i]; 3573 PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &x_t)); 3574 } 3575 if (locA) { 3576 PetscInt subcell; 3577 PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell)); 3578 PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, NULL, &x)); 3579 for (i = 0; i < totDimAux; ++i) al[cind * totDimAux + i] = x[i]; 3580 PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, NULL, &x)); 3581 } 3582 } 3583 PetscCall(DMDestroy(&plex)); 3584 if (locA) PetscCall(DMDestroy(&plexA)); 3585 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 3586 PetscFunctionReturn(PETSC_SUCCESS); 3587 } 3588 3589 /*@C 3590 DMPlexRestoreCellFields - Restore the field values values for a chunk of cells 3591 3592 Input Parameters: 3593 + dm - The `DM` 3594 . cellIS - The cells to include 3595 . locX - A local vector with the solution fields 3596 . locX_t - A local vector with solution field time derivatives, or NULL 3597 - locA - A local vector with auxiliary fields, or NULL 3598 3599 Output Parameters: 3600 + u - The field coefficients 3601 . u_t - The fields derivative coefficients 3602 - a - The auxiliary field coefficients 3603 3604 Level: developer 3605 3606 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()` 3607 @*/ 3608 PetscErrorCode DMPlexRestoreCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a) 3609 { 3610 PetscFunctionBegin; 3611 PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u)); 3612 if (locX_t) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, u_t)); 3613 if (locA) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, a)); 3614 PetscFunctionReturn(PETSC_SUCCESS); 3615 } 3616 3617 static PetscErrorCode DMPlexGetHybridCellFields(DM dm, IS cellIS, Vec locX, Vec locX_t, Vec locA, PetscScalar **u, PetscScalar **u_t, PetscScalar **a) 3618 { 3619 DM plex, plexA = NULL; 3620 DMEnclosureType encAux; 3621 PetscSection section, sectionAux; 3622 PetscDS ds, dsIn; 3623 const PetscInt *cells; 3624 PetscInt cStart, cEnd, numCells, c, totDim, totDimAux, Nf, f; 3625 3626 PetscFunctionBegin; 3627 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3628 PetscValidHeaderSpecific(cellIS, IS_CLASSID, 2); 3629 PetscValidHeaderSpecific(locX, VEC_CLASSID, 3); 3630 if (locX_t) { PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 4); } 3631 if (locA) { PetscValidHeaderSpecific(locA, VEC_CLASSID, 5); } 3632 PetscAssertPointer(u, 6); 3633 PetscAssertPointer(u_t, 7); 3634 PetscAssertPointer(a, 8); 3635 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 3636 numCells = cEnd - cStart; 3637 PetscCall(DMPlexConvertPlex(dm, &plex, PETSC_FALSE)); 3638 PetscCall(DMGetLocalSection(dm, §ion)); 3639 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn)); 3640 PetscCall(PetscDSGetNumFields(dsIn, &Nf)); 3641 PetscCall(PetscDSGetTotalDimension(dsIn, &totDim)); 3642 if (locA) { 3643 DM dmAux; 3644 PetscDS probAux; 3645 3646 PetscCall(VecGetDM(locA, &dmAux)); 3647 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 3648 PetscCall(DMPlexConvertPlex(dmAux, &plexA, PETSC_FALSE)); 3649 PetscCall(DMGetLocalSection(dmAux, §ionAux)); 3650 PetscCall(DMGetDS(dmAux, &probAux)); 3651 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 3652 } 3653 PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u)); 3654 if (locX_t) PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, u_t)); 3655 else { 3656 *u_t = NULL; 3657 } 3658 if (locA) PetscCall(DMGetWorkArray(dm, numCells * totDimAux, MPIU_SCALAR, a)); 3659 else { 3660 *a = NULL; 3661 } 3662 // Loop over cohesive cells 3663 for (c = cStart; c < cEnd; ++c) { 3664 const PetscInt cell = cells ? cells[c] : c; 3665 const PetscInt cind = c - cStart; 3666 PetscScalar *xf = NULL, *xc = NULL, *x = NULL, *xf_t = NULL, *xc_t = NULL; 3667 PetscScalar *ul = &(*u)[cind * totDim], *ul_t = PetscSafePointerPlusOffset(*u_t, cind * totDim); 3668 const PetscInt *cone, *ornt; 3669 PetscInt Nx = 0, Nxf, s; 3670 3671 PetscCall(DMPlexGetCone(dm, cell, &cone)); 3672 PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt)); 3673 // Put in cohesive unknowns 3674 PetscCall(DMPlexVecGetClosure(plex, section, locX, cell, &Nxf, &xf)); 3675 if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, cell, NULL, &xf_t)); 3676 for (f = 0; f < Nf; ++f) { 3677 PetscInt fdofIn, foff, foffIn; 3678 PetscBool cohesive; 3679 3680 PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive)); 3681 if (!cohesive) continue; 3682 PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn)); 3683 PetscCall(PetscDSGetFieldOffsetCohesive(ds, f, &foff)); 3684 PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn)); 3685 for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + i] = xf[foff + i]; 3686 if (locX_t) 3687 for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + i] = xf_t[foff + i]; 3688 Nx += fdofIn; 3689 } 3690 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, cell, &Nxf, &xf)); 3691 if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, cell, NULL, &xf_t)); 3692 // Loop over sides of surface 3693 for (s = 0; s < 2; ++s) { 3694 const PetscInt *support; 3695 const PetscInt face = cone[s]; 3696 PetscInt ssize, ncell, Nxc; 3697 3698 // I don't think I need the face to have 0 orientation in the hybrid cell 3699 //PetscCheck(!ornt[s], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != 0", face, cell, ornt[s]); 3700 PetscCall(DMPlexGetSupport(dm, face, &support)); 3701 PetscCall(DMPlexGetSupportSize(dm, face, &ssize)); 3702 if (support[0] == cell) ncell = support[1]; 3703 else if (support[1] == cell) ncell = support[0]; 3704 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell); 3705 // Get closure of both face and cell, stick in cell for normal fields and face for cohesive fields 3706 PetscCall(DMPlexVecGetClosure(plex, section, locX, ncell, &Nxc, &xc)); 3707 if (locX_t) PetscCall(DMPlexVecGetClosure(plex, section, locX_t, ncell, NULL, &xc_t)); 3708 for (f = 0; f < Nf; ++f) { 3709 PetscInt fdofIn, foffIn; 3710 PetscBool cohesive; 3711 3712 PetscCall(PetscDSGetCohesive(dsIn, f, &cohesive)); 3713 if (cohesive) continue; 3714 PetscCall(PetscDSGetFieldSize(dsIn, f, &fdofIn)); 3715 PetscCall(PetscDSGetFieldOffsetCohesive(dsIn, f, &foffIn)); 3716 for (PetscInt i = 0; i < fdofIn; ++i) ul[foffIn + s * fdofIn + i] = xc[foffIn + i]; 3717 if (locX_t) 3718 for (PetscInt i = 0; i < fdofIn; ++i) ul_t[foffIn + s * fdofIn + i] = xc_t[foffIn + i]; 3719 Nx += fdofIn; 3720 } 3721 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, ncell, &Nxc, &xc)); 3722 if (locX_t) PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, ncell, NULL, &xc_t)); 3723 } 3724 PetscCheck(Nx == totDim, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for cell %" PetscInt_FMT " does not match DS size %" PetscInt_FMT, Nx, cell, totDim); 3725 3726 if (locA) { 3727 PetscScalar *al = &(*a)[cind * totDimAux]; 3728 PetscInt subcell; 3729 3730 PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, cell, &subcell)); 3731 PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subcell, &Nx, &x)); 3732 PetscCheck(Nx == totDimAux, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for subcell %" PetscInt_FMT "does not match DS size %" PetscInt_FMT, Nx, subcell, totDimAux); 3733 for (PetscInt i = 0; i < totDimAux; ++i) al[i] = x[i]; 3734 PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subcell, &Nx, &x)); 3735 } 3736 } 3737 PetscCall(DMDestroy(&plex)); 3738 PetscCall(DMDestroy(&plexA)); 3739 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 3740 PetscFunctionReturn(PETSC_SUCCESS); 3741 } 3742 3743 /* 3744 DMPlexGetHybridFields - Get the field values for the negative side (s = 0) and positive side (s = 1) of the interface 3745 3746 Input Parameters: 3747 + dm - The full domain DM 3748 . dmX - An array of DM for the field, say an auxiliary DM, indexed by s 3749 . dsX - An array of PetscDS for the field, indexed by s 3750 . cellIS - The interface cells for which we want values 3751 . locX - An array of local vectors with the field values, indexed by s 3752 - useCell - Flag to have values come from neighboring cell rather than endcap face 3753 3754 Output Parameter: 3755 . x - An array of field values, indexed by s 3756 3757 Note: 3758 The arrays in `x` will be allocated using `DMGetWorkArray()`, and must be returned using `DMPlexRestoreHybridFields()`. 3759 3760 Level: advanced 3761 3762 .seealso: `DMPlexRestoreHybridFields()`, `DMGetWorkArray()` 3763 */ 3764 static PetscErrorCode DMPlexGetHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[]) 3765 { 3766 DM plexX[2]; 3767 DMEnclosureType encX[2]; 3768 PetscSection sectionX[2]; 3769 const PetscInt *cells; 3770 PetscInt cStart, cEnd, numCells, c, s, totDimX[2]; 3771 3772 PetscFunctionBegin; 3773 PetscAssertPointer(locX, 5); 3774 if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS); 3775 PetscAssertPointer(dmX, 2); 3776 PetscAssertPointer(dsX, 3); 3777 PetscValidHeaderSpecific(cellIS, IS_CLASSID, 4); 3778 PetscAssertPointer(x, 7); 3779 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 3780 numCells = cEnd - cStart; 3781 for (s = 0; s < 2; ++s) { 3782 PetscValidHeaderSpecific(dmX[s], DM_CLASSID, 2); 3783 PetscValidHeaderSpecific(dsX[s], PETSCDS_CLASSID, 3); 3784 PetscValidHeaderSpecific(locX[s], VEC_CLASSID, 5); 3785 PetscCall(DMPlexConvertPlex(dmX[s], &plexX[s], PETSC_FALSE)); 3786 PetscCall(DMGetEnclosureRelation(dmX[s], dm, &encX[s])); 3787 PetscCall(DMGetLocalSection(dmX[s], §ionX[s])); 3788 PetscCall(PetscDSGetTotalDimension(dsX[s], &totDimX[s])); 3789 PetscCall(DMGetWorkArray(dmX[s], numCells * totDimX[s], MPIU_SCALAR, &x[s])); 3790 } 3791 for (c = cStart; c < cEnd; ++c) { 3792 const PetscInt cell = cells ? cells[c] : c; 3793 const PetscInt cind = c - cStart; 3794 const PetscInt *cone, *ornt; 3795 3796 PetscCall(DMPlexGetCone(dm, cell, &cone)); 3797 PetscCall(DMPlexGetConeOrientation(dm, cell, &ornt)); 3798 //PetscCheck(!ornt[0], PETSC_COMM_SELF, PETSC_ERR_SUP, "Face %" PetscInt_FMT " in hybrid cell %" PetscInt_FMT " has orientation %" PetscInt_FMT " != 0", cone[0], cell, ornt[0]); 3799 for (s = 0; s < 2; ++s) { 3800 const PetscInt tdX = totDimX[s]; 3801 PetscScalar *closure = NULL, *xl = &x[s][cind * tdX]; 3802 PetscInt face = cone[s], point = face, subpoint, Nx, i; 3803 3804 if (useCell) { 3805 const PetscInt *support; 3806 PetscInt ssize; 3807 3808 PetscCall(DMPlexGetSupport(dm, face, &support)); 3809 PetscCall(DMPlexGetSupportSize(dm, face, &ssize)); 3810 PetscCheck(ssize == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " from cell %" PetscInt_FMT " has support size %" PetscInt_FMT " != 2", face, cell, ssize); 3811 if (support[0] == cell) point = support[1]; 3812 else if (support[1] == cell) point = support[0]; 3813 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", face, cell); 3814 } 3815 PetscCall(DMGetEnclosurePoint(plexX[s], dm, encX[s], point, &subpoint)); 3816 PetscCall(DMPlexVecGetOrientedClosure_Internal(plexX[s], sectionX[s], PETSC_FALSE, locX[s], subpoint, ornt[s], &Nx, &closure)); 3817 PetscCheck(Nx == tdX, PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Closure size %" PetscInt_FMT " for subpoint %" PetscInt_FMT " does not match DS size %" PetscInt_FMT, Nx, subpoint, tdX); 3818 for (i = 0; i < Nx; ++i) xl[i] = closure[i]; 3819 PetscCall(DMPlexVecRestoreClosure(plexX[s], sectionX[s], locX[s], subpoint, &Nx, &closure)); 3820 } 3821 } 3822 for (s = 0; s < 2; ++s) PetscCall(DMDestroy(&plexX[s])); 3823 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 3824 PetscFunctionReturn(PETSC_SUCCESS); 3825 } 3826 3827 static PetscErrorCode DMPlexRestoreHybridFields(DM dm, DM dmX[], PetscDS dsX[], IS cellIS, Vec locX[], PetscBool useCell, PetscScalar *x[]) 3828 { 3829 PetscFunctionBegin; 3830 if (!locX[0] || !locX[1]) PetscFunctionReturn(PETSC_SUCCESS); 3831 PetscCall(DMRestoreWorkArray(dmX[0], 0, MPIU_SCALAR, &x[0])); 3832 PetscCall(DMRestoreWorkArray(dmX[1], 0, MPIU_SCALAR, &x[1])); 3833 PetscFunctionReturn(PETSC_SUCCESS); 3834 } 3835 3836 /*@C 3837 DMPlexGetFaceFields - Retrieve the field values values for a chunk of faces 3838 3839 Input Parameters: 3840 + dm - The `DM` 3841 . fStart - The first face to include 3842 . fEnd - The first face to exclude 3843 . locX - A local vector with the solution fields 3844 . locX_t - A local vector with solution field time derivatives, or NULL 3845 . faceGeometry - A local vector with face geometry 3846 . cellGeometry - A local vector with cell geometry 3847 - locGrad - A local vector with field gradients, or NULL 3848 3849 Output Parameters: 3850 + Nface - The number of faces with field values 3851 . uL - The field values at the left side of the face 3852 - uR - The field values at the right side of the face 3853 3854 Level: developer 3855 3856 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()` 3857 @*/ 3858 PetscErrorCode DMPlexGetFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR) 3859 { 3860 DM dmFace, dmCell, dmGrad = NULL; 3861 PetscSection section; 3862 PetscDS prob; 3863 DMLabel ghostLabel; 3864 const PetscScalar *facegeom, *cellgeom, *x, *lgrad; 3865 PetscBool *isFE; 3866 PetscInt dim, Nf, f, Nc, numFaces = fEnd - fStart, iface, face; 3867 3868 PetscFunctionBegin; 3869 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 3870 PetscValidHeaderSpecific(locX, VEC_CLASSID, 4); 3871 if (locX_t) PetscValidHeaderSpecific(locX_t, VEC_CLASSID, 5); 3872 PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 6); 3873 PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 7); 3874 if (locGrad) PetscValidHeaderSpecific(locGrad, VEC_CLASSID, 8); 3875 PetscAssertPointer(uL, 10); 3876 PetscAssertPointer(uR, 11); 3877 PetscCall(DMGetDimension(dm, &dim)); 3878 PetscCall(DMGetDS(dm, &prob)); 3879 PetscCall(DMGetLocalSection(dm, §ion)); 3880 PetscCall(PetscDSGetNumFields(prob, &Nf)); 3881 PetscCall(PetscDSGetTotalComponents(prob, &Nc)); 3882 PetscCall(PetscMalloc1(Nf, &isFE)); 3883 for (f = 0; f < Nf; ++f) { 3884 PetscObject obj; 3885 PetscClassId id; 3886 3887 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 3888 PetscCall(PetscObjectGetClassId(obj, &id)); 3889 if (id == PETSCFE_CLASSID) { 3890 isFE[f] = PETSC_TRUE; 3891 } else if (id == PETSCFV_CLASSID) { 3892 isFE[f] = PETSC_FALSE; 3893 } else { 3894 isFE[f] = PETSC_FALSE; 3895 } 3896 } 3897 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 3898 PetscCall(VecGetArrayRead(locX, &x)); 3899 PetscCall(VecGetDM(faceGeometry, &dmFace)); 3900 PetscCall(VecGetArrayRead(faceGeometry, &facegeom)); 3901 PetscCall(VecGetDM(cellGeometry, &dmCell)); 3902 PetscCall(VecGetArrayRead(cellGeometry, &cellgeom)); 3903 if (locGrad) { 3904 PetscCall(VecGetDM(locGrad, &dmGrad)); 3905 PetscCall(VecGetArrayRead(locGrad, &lgrad)); 3906 } 3907 PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uL)); 3908 PetscCall(DMGetWorkArray(dm, numFaces * Nc, MPIU_SCALAR, uR)); 3909 /* Right now just eat the extra work for FE (could make a cell loop) */ 3910 for (face = fStart, iface = 0; face < fEnd; ++face) { 3911 const PetscInt *cells; 3912 PetscFVFaceGeom *fg; 3913 PetscFVCellGeom *cgL, *cgR; 3914 PetscScalar *xL, *xR, *gL, *gR; 3915 PetscScalar *uLl = *uL, *uRl = *uR; 3916 PetscInt ghost, nsupp, nchild; 3917 3918 PetscCall(DMLabelGetValue(ghostLabel, face, &ghost)); 3919 PetscCall(DMPlexGetSupportSize(dm, face, &nsupp)); 3920 PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL)); 3921 if (ghost >= 0 || nsupp > 2 || nchild > 0) continue; 3922 PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg)); 3923 PetscCall(DMPlexGetSupport(dm, face, &cells)); 3924 PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL)); 3925 PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR)); 3926 for (f = 0; f < Nf; ++f) { 3927 PetscInt off; 3928 3929 PetscCall(PetscDSGetComponentOffset(prob, f, &off)); 3930 if (isFE[f]) { 3931 const PetscInt *cone; 3932 PetscInt comp, coneSizeL, coneSizeR, faceLocL, faceLocR, ldof, rdof, d; 3933 3934 xL = xR = NULL; 3935 PetscCall(PetscSectionGetFieldComponents(section, f, &comp)); 3936 PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL)); 3937 PetscCall(DMPlexVecGetClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR)); 3938 PetscCall(DMPlexGetCone(dm, cells[0], &cone)); 3939 PetscCall(DMPlexGetConeSize(dm, cells[0], &coneSizeL)); 3940 for (faceLocL = 0; faceLocL < coneSizeL; ++faceLocL) 3941 if (cone[faceLocL] == face) break; 3942 PetscCall(DMPlexGetCone(dm, cells[1], &cone)); 3943 PetscCall(DMPlexGetConeSize(dm, cells[1], &coneSizeR)); 3944 for (faceLocR = 0; faceLocR < coneSizeR; ++faceLocR) 3945 if (cone[faceLocR] == face) break; 3946 PetscCheck(faceLocL != coneSizeL || faceLocR != coneSizeR, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find face %" PetscInt_FMT " in cone of cell %" PetscInt_FMT " or cell %" PetscInt_FMT, face, cells[0], cells[1]); 3947 /* Check that FEM field has values in the right cell (sometimes its an FV ghost cell) */ 3948 /* TODO: this is a hack that might not be right for nonconforming */ 3949 if (faceLocL < coneSizeL) { 3950 PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocL, xL, &uLl[iface * Nc + off])); 3951 if (rdof == ldof && faceLocR < coneSizeR) PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off])); 3952 else { 3953 for (d = 0; d < comp; ++d) uRl[iface * Nc + off + d] = uLl[iface * Nc + off + d]; 3954 } 3955 } else { 3956 PetscCall(PetscFEEvaluateFaceFields_Internal(prob, f, faceLocR, xR, &uRl[iface * Nc + off])); 3957 PetscCall(PetscSectionGetFieldComponents(section, f, &comp)); 3958 for (d = 0; d < comp; ++d) uLl[iface * Nc + off + d] = uRl[iface * Nc + off + d]; 3959 } 3960 PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[0], &ldof, (PetscScalar **)&xL)); 3961 PetscCall(DMPlexVecRestoreClosure(dm, section, locX, cells[1], &rdof, (PetscScalar **)&xR)); 3962 } else { 3963 PetscFV fv; 3964 PetscInt numComp, c; 3965 3966 PetscCall(PetscDSGetDiscretization(prob, f, (PetscObject *)&fv)); 3967 PetscCall(PetscFVGetNumComponents(fv, &numComp)); 3968 PetscCall(DMPlexPointLocalFieldRead(dm, cells[0], f, x, &xL)); 3969 PetscCall(DMPlexPointLocalFieldRead(dm, cells[1], f, x, &xR)); 3970 if (dmGrad) { 3971 PetscReal dxL[3], dxR[3]; 3972 3973 PetscCall(DMPlexPointLocalRead(dmGrad, cells[0], lgrad, &gL)); 3974 PetscCall(DMPlexPointLocalRead(dmGrad, cells[1], lgrad, &gR)); 3975 DMPlex_WaxpyD_Internal(dim, -1, cgL->centroid, fg->centroid, dxL); 3976 DMPlex_WaxpyD_Internal(dim, -1, cgR->centroid, fg->centroid, dxR); 3977 for (c = 0; c < numComp; ++c) { 3978 uLl[iface * Nc + off + c] = xL[c] + DMPlex_DotD_Internal(dim, &gL[c * dim], dxL); 3979 uRl[iface * Nc + off + c] = xR[c] + DMPlex_DotD_Internal(dim, &gR[c * dim], dxR); 3980 } 3981 } else { 3982 for (c = 0; c < numComp; ++c) { 3983 uLl[iface * Nc + off + c] = xL[c]; 3984 uRl[iface * Nc + off + c] = xR[c]; 3985 } 3986 } 3987 } 3988 } 3989 ++iface; 3990 } 3991 *Nface = iface; 3992 PetscCall(VecRestoreArrayRead(locX, &x)); 3993 PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom)); 3994 PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom)); 3995 if (locGrad) PetscCall(VecRestoreArrayRead(locGrad, &lgrad)); 3996 PetscCall(PetscFree(isFE)); 3997 PetscFunctionReturn(PETSC_SUCCESS); 3998 } 3999 4000 /*@C 4001 DMPlexRestoreFaceFields - Restore the field values values for a chunk of faces 4002 4003 Input Parameters: 4004 + dm - The `DM` 4005 . fStart - The first face to include 4006 . fEnd - The first face to exclude 4007 . locX - A local vector with the solution fields 4008 . locX_t - A local vector with solution field time derivatives, or NULL 4009 . faceGeometry - A local vector with face geometry 4010 . cellGeometry - A local vector with cell geometry 4011 - locGrad - A local vector with field gradients, or NULL 4012 4013 Output Parameters: 4014 + Nface - The number of faces with field values 4015 . uL - The field values at the left side of the face 4016 - uR - The field values at the right side of the face 4017 4018 Level: developer 4019 4020 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()` 4021 @*/ 4022 PetscErrorCode DMPlexRestoreFaceFields(DM dm, PetscInt fStart, PetscInt fEnd, Vec locX, Vec locX_t, Vec faceGeometry, Vec cellGeometry, Vec locGrad, PetscInt *Nface, PetscScalar **uL, PetscScalar **uR) 4023 { 4024 PetscFunctionBegin; 4025 PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uL)); 4026 PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, uR)); 4027 PetscFunctionReturn(PETSC_SUCCESS); 4028 } 4029 4030 /*@C 4031 DMPlexGetFaceGeometry - Retrieve the geometric values for a chunk of faces 4032 4033 Input Parameters: 4034 + dm - The `DM` 4035 . fStart - The first face to include 4036 . fEnd - The first face to exclude 4037 . faceGeometry - A local vector with face geometry 4038 - cellGeometry - A local vector with cell geometry 4039 4040 Output Parameters: 4041 + Nface - The number of faces with field values 4042 . fgeom - The extract the face centroid and normal 4043 - vol - The cell volume 4044 4045 Level: developer 4046 4047 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellFields()` 4048 @*/ 4049 PetscErrorCode DMPlexGetFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol) 4050 { 4051 DM dmFace, dmCell; 4052 DMLabel ghostLabel; 4053 const PetscScalar *facegeom, *cellgeom; 4054 PetscInt dim, numFaces = fEnd - fStart, iface, face; 4055 4056 PetscFunctionBegin; 4057 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 4058 PetscValidHeaderSpecific(faceGeometry, VEC_CLASSID, 4); 4059 PetscValidHeaderSpecific(cellGeometry, VEC_CLASSID, 5); 4060 PetscAssertPointer(fgeom, 7); 4061 PetscAssertPointer(vol, 8); 4062 PetscCall(DMGetDimension(dm, &dim)); 4063 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 4064 PetscCall(VecGetDM(faceGeometry, &dmFace)); 4065 PetscCall(VecGetArrayRead(faceGeometry, &facegeom)); 4066 PetscCall(VecGetDM(cellGeometry, &dmCell)); 4067 PetscCall(VecGetArrayRead(cellGeometry, &cellgeom)); 4068 PetscCall(PetscMalloc1(numFaces, fgeom)); 4069 PetscCall(DMGetWorkArray(dm, numFaces * 2, MPIU_SCALAR, vol)); 4070 for (face = fStart, iface = 0; face < fEnd; ++face) { 4071 const PetscInt *cells; 4072 PetscFVFaceGeom *fg; 4073 PetscFVCellGeom *cgL, *cgR; 4074 PetscFVFaceGeom *fgeoml = *fgeom; 4075 PetscReal *voll = *vol; 4076 PetscInt ghost, d, nchild, nsupp; 4077 4078 PetscCall(DMLabelGetValue(ghostLabel, face, &ghost)); 4079 PetscCall(DMPlexGetSupportSize(dm, face, &nsupp)); 4080 PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL)); 4081 if (ghost >= 0 || nsupp > 2 || nchild > 0) continue; 4082 PetscCall(DMPlexPointLocalRead(dmFace, face, facegeom, &fg)); 4083 PetscCall(DMPlexGetSupport(dm, face, &cells)); 4084 PetscCall(DMPlexPointLocalRead(dmCell, cells[0], cellgeom, &cgL)); 4085 PetscCall(DMPlexPointLocalRead(dmCell, cells[1], cellgeom, &cgR)); 4086 for (d = 0; d < dim; ++d) { 4087 fgeoml[iface].centroid[d] = fg->centroid[d]; 4088 fgeoml[iface].normal[d] = fg->normal[d]; 4089 } 4090 voll[iface * 2 + 0] = cgL->volume; 4091 voll[iface * 2 + 1] = cgR->volume; 4092 ++iface; 4093 } 4094 *Nface = iface; 4095 PetscCall(VecRestoreArrayRead(faceGeometry, &facegeom)); 4096 PetscCall(VecRestoreArrayRead(cellGeometry, &cellgeom)); 4097 PetscFunctionReturn(PETSC_SUCCESS); 4098 } 4099 4100 /*@C 4101 DMPlexRestoreFaceGeometry - Restore the field values values for a chunk of faces 4102 4103 Input Parameters: 4104 + dm - The `DM` 4105 . fStart - The first face to include 4106 . fEnd - The first face to exclude 4107 . faceGeometry - A local vector with face geometry 4108 - cellGeometry - A local vector with cell geometry 4109 4110 Output Parameters: 4111 + Nface - The number of faces with field values 4112 . fgeom - The extract the face centroid and normal 4113 - vol - The cell volume 4114 4115 Level: developer 4116 4117 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetFaceFields()` 4118 @*/ 4119 PetscErrorCode DMPlexRestoreFaceGeometry(DM dm, PetscInt fStart, PetscInt fEnd, Vec faceGeometry, Vec cellGeometry, PetscInt *Nface, PetscFVFaceGeom **fgeom, PetscReal **vol) 4120 { 4121 PetscFunctionBegin; 4122 PetscCall(PetscFree(*fgeom)); 4123 PetscCall(DMRestoreWorkArray(dm, 0, MPIU_REAL, vol)); 4124 PetscFunctionReturn(PETSC_SUCCESS); 4125 } 4126 4127 PetscErrorCode DMSNESGetFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom) 4128 { 4129 char composeStr[33] = {0}; 4130 PetscObjectId id; 4131 PetscContainer container; 4132 4133 PetscFunctionBegin; 4134 PetscCall(PetscObjectGetId((PetscObject)quad, &id)); 4135 PetscCall(PetscSNPrintf(composeStr, 32, "DMSNESGetFEGeom_%" PetscInt64_FMT "\n", id)); 4136 PetscCall(PetscObjectQuery((PetscObject)pointIS, composeStr, (PetscObject *)&container)); 4137 if (container) { 4138 PetscCall(PetscContainerGetPointer(container, (void **)geom)); 4139 } else { 4140 PetscCall(DMFieldCreateFEGeom(coordField, pointIS, quad, faceData, geom)); 4141 PetscCall(PetscContainerCreate(PETSC_COMM_SELF, &container)); 4142 PetscCall(PetscContainerSetPointer(container, (void *)*geom)); 4143 PetscCall(PetscContainerSetUserDestroy(container, PetscContainerUserDestroy_PetscFEGeom)); 4144 PetscCall(PetscObjectCompose((PetscObject)pointIS, composeStr, (PetscObject)container)); 4145 PetscCall(PetscContainerDestroy(&container)); 4146 } 4147 PetscFunctionReturn(PETSC_SUCCESS); 4148 } 4149 4150 PetscErrorCode DMSNESRestoreFEGeom(DMField coordField, IS pointIS, PetscQuadrature quad, PetscBool faceData, PetscFEGeom **geom) 4151 { 4152 PetscFunctionBegin; 4153 *geom = NULL; 4154 PetscFunctionReturn(PETSC_SUCCESS); 4155 } 4156 4157 PetscErrorCode DMPlexComputeResidual_Patch_Internal(DM dm, PetscSection section, IS cellIS, PetscReal t, Vec locX, Vec locX_t, Vec locF, void *user) 4158 { 4159 DM_Plex *mesh = (DM_Plex *)dm->data; 4160 const char *name = "Residual"; 4161 DM dmAux = NULL; 4162 DMLabel ghostLabel = NULL; 4163 PetscDS prob = NULL; 4164 PetscDS probAux = NULL; 4165 PetscBool useFEM = PETSC_FALSE; 4166 PetscBool isImplicit = (locX_t || t == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE; 4167 DMField coordField = NULL; 4168 Vec locA; 4169 PetscScalar *u = NULL, *u_t, *a, *uL = NULL, *uR = NULL; 4170 IS chunkIS; 4171 const PetscInt *cells; 4172 PetscInt cStart, cEnd, numCells; 4173 PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, chunk, fStart, fEnd; 4174 PetscInt maxDegree = PETSC_MAX_INT; 4175 PetscFormKey key; 4176 PetscQuadrature affineQuad = NULL, *quads = NULL; 4177 PetscFEGeom *affineGeom = NULL, **geoms = NULL; 4178 4179 PetscFunctionBegin; 4180 PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 4181 /* FEM+FVM */ 4182 /* 1: Get sizes from dm and dmAux */ 4183 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 4184 PetscCall(DMGetDS(dm, &prob)); 4185 PetscCall(PetscDSGetNumFields(prob, &Nf)); 4186 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 4187 PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &locA)); 4188 if (locA) { 4189 PetscCall(VecGetDM(locA, &dmAux)); 4190 PetscCall(DMGetDS(dmAux, &probAux)); 4191 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 4192 } 4193 /* 2: Get geometric data */ 4194 for (f = 0; f < Nf; ++f) { 4195 PetscObject obj; 4196 PetscClassId id; 4197 PetscBool fimp; 4198 4199 PetscCall(PetscDSGetImplicit(prob, f, &fimp)); 4200 if (isImplicit != fimp) continue; 4201 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 4202 PetscCall(PetscObjectGetClassId(obj, &id)); 4203 if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE; 4204 PetscCheck(id != PETSCFV_CLASSID, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Use of FVM with PCPATCH not yet implemented"); 4205 } 4206 if (useFEM) { 4207 PetscCall(DMGetCoordinateField(dm, &coordField)); 4208 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 4209 if (maxDegree <= 1) { 4210 PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad)); 4211 if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 4212 } else { 4213 PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms)); 4214 for (f = 0; f < Nf; ++f) { 4215 PetscObject obj; 4216 PetscClassId id; 4217 PetscBool fimp; 4218 4219 PetscCall(PetscDSGetImplicit(prob, f, &fimp)); 4220 if (isImplicit != fimp) continue; 4221 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 4222 PetscCall(PetscObjectGetClassId(obj, &id)); 4223 if (id == PETSCFE_CLASSID) { 4224 PetscFE fe = (PetscFE)obj; 4225 4226 PetscCall(PetscFEGetQuadrature(fe, &quads[f])); 4227 PetscCall(PetscObjectReference((PetscObject)quads[f])); 4228 PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 4229 } 4230 } 4231 } 4232 } 4233 /* Loop over chunks */ 4234 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 4235 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 4236 if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS)); 4237 numCells = cEnd - cStart; 4238 numChunks = 1; 4239 cellChunkSize = numCells / numChunks; 4240 numChunks = PetscMin(1, numCells); 4241 key.label = NULL; 4242 key.value = 0; 4243 key.part = 0; 4244 for (chunk = 0; chunk < numChunks; ++chunk) { 4245 PetscScalar *elemVec, *fluxL = NULL, *fluxR = NULL; 4246 PetscReal *vol = NULL; 4247 PetscFVFaceGeom *fgeom = NULL; 4248 PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c; 4249 PetscInt numFaces = 0; 4250 4251 /* Extract field coefficients */ 4252 if (useFEM) { 4253 PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells)); 4254 PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a)); 4255 PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec)); 4256 PetscCall(PetscArrayzero(elemVec, numCells * totDim)); 4257 } 4258 /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */ 4259 /* Loop over fields */ 4260 for (f = 0; f < Nf; ++f) { 4261 PetscObject obj; 4262 PetscClassId id; 4263 PetscBool fimp; 4264 PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset; 4265 4266 key.field = f; 4267 PetscCall(PetscDSGetImplicit(prob, f, &fimp)); 4268 if (isImplicit != fimp) continue; 4269 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 4270 PetscCall(PetscObjectGetClassId(obj, &id)); 4271 if (id == PETSCFE_CLASSID) { 4272 PetscFE fe = (PetscFE)obj; 4273 PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f]; 4274 PetscFEGeom *chunkGeom = NULL; 4275 PetscQuadrature quad = affineQuad ? affineQuad : quads[f]; 4276 PetscInt Nq, Nb; 4277 4278 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 4279 PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL)); 4280 PetscCall(PetscFEGetDimension(fe, &Nb)); 4281 blockSize = Nb; 4282 batchSize = numBlocks * blockSize; 4283 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 4284 numChunks = numCells / (numBatches * batchSize); 4285 Ne = numChunks * numBatches * batchSize; 4286 Nr = numCells % (numBatches * batchSize); 4287 offset = numCells - Nr; 4288 /* Integrate FE residual to get elemVec (need fields at quadrature points) */ 4289 /* For FV, I think we use a P0 basis and the cell coefficients (for subdivided cells, we can tweak the basis tabulation to be the indicator function) */ 4290 PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom)); 4291 PetscCall(PetscFEIntegrateResidual(prob, key, Ne, chunkGeom, u, u_t, probAux, a, t, elemVec)); 4292 PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom)); 4293 PetscCall(PetscFEIntegrateResidual(prob, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), probAux, &a[offset * totDimAux], t, &elemVec[offset * totDim])); 4294 PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom)); 4295 } else if (id == PETSCFV_CLASSID) { 4296 PetscFV fv = (PetscFV)obj; 4297 4298 Ne = numFaces; 4299 /* Riemann solve over faces (need fields at face centroids) */ 4300 /* We need to evaluate FE fields at those coordinates */ 4301 PetscCall(PetscFVIntegrateRHSFunction(fv, prob, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR)); 4302 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f); 4303 } 4304 /* Loop over domain */ 4305 if (useFEM) { 4306 /* Add elemVec to locX */ 4307 for (c = cS; c < cE; ++c) { 4308 const PetscInt cell = cells ? cells[c] : c; 4309 const PetscInt cind = c - cStart; 4310 4311 if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim])); 4312 if (ghostLabel) { 4313 PetscInt ghostVal; 4314 4315 PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal)); 4316 if (ghostVal > 0) continue; 4317 } 4318 PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES)); 4319 } 4320 } 4321 /* Handle time derivative */ 4322 if (locX_t) { 4323 PetscScalar *x_t, *fa; 4324 4325 PetscCall(VecGetArray(locF, &fa)); 4326 PetscCall(VecGetArray(locX_t, &x_t)); 4327 for (f = 0; f < Nf; ++f) { 4328 PetscFV fv; 4329 PetscObject obj; 4330 PetscClassId id; 4331 PetscInt pdim, d; 4332 4333 PetscCall(PetscDSGetDiscretization(prob, f, &obj)); 4334 PetscCall(PetscObjectGetClassId(obj, &id)); 4335 if (id != PETSCFV_CLASSID) continue; 4336 fv = (PetscFV)obj; 4337 PetscCall(PetscFVGetNumComponents(fv, &pdim)); 4338 for (c = cS; c < cE; ++c) { 4339 const PetscInt cell = cells ? cells[c] : c; 4340 PetscScalar *u_t, *r; 4341 4342 if (ghostLabel) { 4343 PetscInt ghostVal; 4344 4345 PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal)); 4346 if (ghostVal > 0) continue; 4347 } 4348 PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t)); 4349 PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r)); 4350 for (d = 0; d < pdim; ++d) r[d] += u_t[d]; 4351 } 4352 } 4353 PetscCall(VecRestoreArray(locX_t, &x_t)); 4354 PetscCall(VecRestoreArray(locF, &fa)); 4355 } 4356 if (useFEM) { 4357 PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a)); 4358 PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec)); 4359 } 4360 } 4361 if (useFEM) PetscCall(ISDestroy(&chunkIS)); 4362 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 4363 /* TODO Could include boundary residual here (see DMPlexComputeResidual_Internal) */ 4364 if (useFEM) { 4365 if (maxDegree <= 1) { 4366 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 4367 PetscCall(PetscQuadratureDestroy(&affineQuad)); 4368 } else { 4369 for (f = 0; f < Nf; ++f) { 4370 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 4371 PetscCall(PetscQuadratureDestroy(&quads[f])); 4372 } 4373 PetscCall(PetscFree2(quads, geoms)); 4374 } 4375 } 4376 PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 4377 PetscFunctionReturn(PETSC_SUCCESS); 4378 } 4379 4380 /* 4381 We always assemble JacP, and if the matrix is different from Jac and two different sets of point functions are provided, we also assemble Jac 4382 4383 X - The local solution vector 4384 X_t - The local solution time derivative vector, or NULL 4385 */ 4386 PetscErrorCode DMPlexComputeJacobian_Patch_Internal(DM dm, PetscSection section, PetscSection globalSection, IS cellIS, PetscReal t, PetscReal X_tShift, Vec X, Vec X_t, Mat Jac, Mat JacP, void *ctx) 4387 { 4388 DM_Plex *mesh = (DM_Plex *)dm->data; 4389 const char *name = "Jacobian", *nameP = "JacobianPre"; 4390 DM dmAux = NULL; 4391 PetscDS prob, probAux = NULL; 4392 PetscSection sectionAux = NULL; 4393 Vec A; 4394 DMField coordField; 4395 PetscFEGeom *cgeomFEM; 4396 PetscQuadrature qGeom = NULL; 4397 Mat J = Jac, JP = JacP; 4398 PetscScalar *work, *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL, *elemMatD = NULL; 4399 PetscBool hasJac, hasPrec, hasDyn, assembleJac, *isFE, hasFV = PETSC_FALSE; 4400 const PetscInt *cells; 4401 PetscFormKey key; 4402 PetscInt Nf, fieldI, fieldJ, maxDegree, numCells, cStart, cEnd, numChunks, chunkSize, chunk, totDim, totDimAux = 0, sz, wsz, off = 0, offCell = 0; 4403 4404 PetscFunctionBegin; 4405 PetscCall(ISGetLocalSize(cellIS, &numCells)); 4406 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 4407 PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 4408 PetscCall(DMGetDS(dm, &prob)); 4409 PetscCall(DMGetAuxiliaryVec(dm, NULL, 0, 0, &A)); 4410 if (A) { 4411 PetscCall(VecGetDM(A, &dmAux)); 4412 PetscCall(DMGetLocalSection(dmAux, §ionAux)); 4413 PetscCall(DMGetDS(dmAux, &probAux)); 4414 } 4415 /* Get flags */ 4416 PetscCall(PetscDSGetNumFields(prob, &Nf)); 4417 PetscCall(DMGetWorkArray(dm, Nf, MPIU_BOOL, &isFE)); 4418 for (fieldI = 0; fieldI < Nf; ++fieldI) { 4419 PetscObject disc; 4420 PetscClassId id; 4421 PetscCall(PetscDSGetDiscretization(prob, fieldI, &disc)); 4422 PetscCall(PetscObjectGetClassId(disc, &id)); 4423 if (id == PETSCFE_CLASSID) { 4424 isFE[fieldI] = PETSC_TRUE; 4425 } else if (id == PETSCFV_CLASSID) { 4426 hasFV = PETSC_TRUE; 4427 isFE[fieldI] = PETSC_FALSE; 4428 } 4429 } 4430 PetscCall(PetscDSHasJacobian(prob, &hasJac)); 4431 PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec)); 4432 PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn)); 4433 assembleJac = hasJac && hasPrec && (Jac != JacP) ? PETSC_TRUE : PETSC_FALSE; 4434 hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE; 4435 if (hasFV) PetscCall(MatSetOption(JP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); /* No allocated space for FV stuff, so ignore the zero entries */ 4436 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 4437 if (probAux) PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 4438 /* Compute batch sizes */ 4439 if (isFE[0]) { 4440 PetscFE fe; 4441 PetscQuadrature q; 4442 PetscInt numQuadPoints, numBatches, batchSize, numBlocks, blockSize, Nb; 4443 4444 PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe)); 4445 PetscCall(PetscFEGetQuadrature(fe, &q)); 4446 PetscCall(PetscQuadratureGetData(q, NULL, NULL, &numQuadPoints, NULL, NULL)); 4447 PetscCall(PetscFEGetDimension(fe, &Nb)); 4448 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 4449 blockSize = Nb * numQuadPoints; 4450 batchSize = numBlocks * blockSize; 4451 chunkSize = numBatches * batchSize; 4452 numChunks = numCells / chunkSize + numCells % chunkSize; 4453 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 4454 } else { 4455 chunkSize = numCells; 4456 numChunks = 1; 4457 } 4458 /* Get work space */ 4459 wsz = (((X ? 1 : 0) + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize; 4460 PetscCall(DMGetWorkArray(dm, wsz, MPIU_SCALAR, &work)); 4461 PetscCall(PetscArrayzero(work, wsz)); 4462 off = 0; 4463 u = X ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL; 4464 u_t = X_t ? (sz = chunkSize * totDim, off += sz, work + off - sz) : NULL; 4465 a = dmAux ? (sz = chunkSize * totDimAux, off += sz, work + off - sz) : NULL; 4466 elemMat = hasJac ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL; 4467 elemMatP = hasPrec ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL; 4468 elemMatD = hasDyn ? (sz = chunkSize * totDim * totDim, off += sz, work + off - sz) : NULL; 4469 PetscCheck(off == wsz, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Error is workspace size %" PetscInt_FMT " should be %" PetscInt_FMT, off, wsz); 4470 /* Setup geometry */ 4471 PetscCall(DMGetCoordinateField(dm, &coordField)); 4472 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 4473 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom)); 4474 if (!qGeom) { 4475 PetscFE fe; 4476 4477 PetscCall(PetscDSGetDiscretization(prob, 0, (PetscObject *)&fe)); 4478 PetscCall(PetscFEGetQuadrature(fe, &qGeom)); 4479 PetscCall(PetscObjectReference((PetscObject)qGeom)); 4480 } 4481 PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 4482 /* Compute volume integrals */ 4483 if (assembleJac) PetscCall(MatZeroEntries(J)); 4484 PetscCall(MatZeroEntries(JP)); 4485 key.label = NULL; 4486 key.value = 0; 4487 key.part = 0; 4488 for (chunk = 0; chunk < numChunks; ++chunk, offCell += chunkSize) { 4489 const PetscInt Ncell = PetscMin(chunkSize, numCells - offCell); 4490 PetscInt c; 4491 4492 /* Extract values */ 4493 for (c = 0; c < Ncell; ++c) { 4494 const PetscInt cell = cells ? cells[c + offCell] : c + offCell; 4495 PetscScalar *x = NULL, *x_t = NULL; 4496 PetscInt i; 4497 4498 if (X) { 4499 PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x)); 4500 for (i = 0; i < totDim; ++i) u[c * totDim + i] = x[i]; 4501 PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x)); 4502 } 4503 if (X_t) { 4504 PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t)); 4505 for (i = 0; i < totDim; ++i) u_t[c * totDim + i] = x_t[i]; 4506 PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t)); 4507 } 4508 if (dmAux) { 4509 PetscCall(DMPlexVecGetClosure(dmAux, sectionAux, A, cell, NULL, &x)); 4510 for (i = 0; i < totDimAux; ++i) a[c * totDimAux + i] = x[i]; 4511 PetscCall(DMPlexVecRestoreClosure(dmAux, sectionAux, A, cell, NULL, &x)); 4512 } 4513 } 4514 for (fieldI = 0; fieldI < Nf; ++fieldI) { 4515 PetscFE fe; 4516 PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe)); 4517 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 4518 key.field = fieldI * Nf + fieldJ; 4519 if (hasJac) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMat)); 4520 if (hasPrec) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatP)); 4521 if (hasDyn) PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ncell, cgeomFEM, u, u_t, probAux, a, t, X_tShift, elemMatD)); 4522 } 4523 /* For finite volume, add the identity */ 4524 if (!isFE[fieldI]) { 4525 PetscFV fv; 4526 PetscInt eOffset = 0, Nc, fc, foff; 4527 4528 PetscCall(PetscDSGetFieldOffset(prob, fieldI, &foff)); 4529 PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv)); 4530 PetscCall(PetscFVGetNumComponents(fv, &Nc)); 4531 for (c = 0; c < chunkSize; ++c, eOffset += totDim * totDim) { 4532 for (fc = 0; fc < Nc; ++fc) { 4533 const PetscInt i = foff + fc; 4534 if (hasJac) elemMat[eOffset + i * totDim + i] = 1.0; 4535 if (hasPrec) elemMatP[eOffset + i * totDim + i] = 1.0; 4536 } 4537 } 4538 } 4539 } 4540 /* Add contribution from X_t */ 4541 if (hasDyn) { 4542 for (c = 0; c < chunkSize * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c]; 4543 } 4544 /* Insert values into matrix */ 4545 for (c = 0; c < Ncell; ++c) { 4546 const PetscInt cell = cells ? cells[c + offCell] : c + offCell; 4547 if (mesh->printFEM > 1) { 4548 if (hasJac) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[(c - cStart) * totDim * totDim])); 4549 if (hasPrec) PetscCall(DMPrintCellMatrix(cell, nameP, totDim, totDim, &elemMatP[(c - cStart) * totDim * totDim])); 4550 } 4551 if (assembleJac) PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES)); 4552 PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JP, cell, &elemMat[(c - cStart) * totDim * totDim], ADD_VALUES)); 4553 } 4554 } 4555 /* Cleanup */ 4556 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 4557 PetscCall(PetscQuadratureDestroy(&qGeom)); 4558 if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE)); 4559 PetscCall(DMRestoreWorkArray(dm, Nf, MPIU_BOOL, &isFE)); 4560 PetscCall(DMRestoreWorkArray(dm, ((1 + (X_t ? 1 : 0) + (dmAux ? 1 : 0)) * totDim + ((hasJac ? 1 : 0) + (hasPrec ? 1 : 0) + (hasDyn ? 1 : 0)) * totDim * totDim) * chunkSize, MPIU_SCALAR, &work)); 4561 /* Compute boundary integrals */ 4562 /* PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, ctx)); */ 4563 /* Assemble matrix */ 4564 if (assembleJac) { 4565 PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY)); 4566 PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY)); 4567 } 4568 PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY)); 4569 PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY)); 4570 PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 4571 PetscFunctionReturn(PETSC_SUCCESS); 4572 } 4573 4574 /******** FEM Assembly Function ********/ 4575 4576 static PetscErrorCode DMConvertPlex_Internal(DM dm, DM *plex, PetscBool copy) 4577 { 4578 PetscBool isPlex; 4579 4580 PetscFunctionBegin; 4581 PetscCall(PetscObjectTypeCompare((PetscObject)dm, DMPLEX, &isPlex)); 4582 if (isPlex) { 4583 *plex = dm; 4584 PetscCall(PetscObjectReference((PetscObject)dm)); 4585 } else { 4586 PetscCall(PetscObjectQuery((PetscObject)dm, "dm_plex", (PetscObject *)plex)); 4587 if (!*plex) { 4588 PetscCall(DMConvert(dm, DMPLEX, plex)); 4589 PetscCall(PetscObjectCompose((PetscObject)dm, "dm_plex", (PetscObject)*plex)); 4590 } else { 4591 PetscCall(PetscObjectReference((PetscObject)*plex)); 4592 } 4593 if (copy) PetscCall(DMCopyAuxiliaryVec(dm, *plex)); 4594 } 4595 PetscFunctionReturn(PETSC_SUCCESS); 4596 } 4597 4598 /*@ 4599 DMPlexGetGeometryFVM - Return precomputed geometric data 4600 4601 Collective 4602 4603 Input Parameter: 4604 . dm - The `DM` 4605 4606 Output Parameters: 4607 + facegeom - The values precomputed from face geometry 4608 . cellgeom - The values precomputed from cell geometry 4609 - minRadius - The minimum radius over the mesh of an inscribed sphere in a cell 4610 4611 Level: developer 4612 4613 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMTSSetRHSFunctionLocal()` 4614 @*/ 4615 PetscErrorCode DMPlexGetGeometryFVM(DM dm, Vec *facegeom, Vec *cellgeom, PetscReal *minRadius) 4616 { 4617 DM plex; 4618 4619 PetscFunctionBegin; 4620 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 4621 PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE)); 4622 PetscCall(DMPlexGetDataFVM(plex, NULL, cellgeom, facegeom, NULL)); 4623 if (minRadius) PetscCall(DMPlexGetMinRadius(plex, minRadius)); 4624 PetscCall(DMDestroy(&plex)); 4625 PetscFunctionReturn(PETSC_SUCCESS); 4626 } 4627 4628 /*@ 4629 DMPlexGetGradientDM - Return gradient data layout 4630 4631 Collective 4632 4633 Input Parameters: 4634 + dm - The `DM` 4635 - fv - The `PetscFV` 4636 4637 Output Parameter: 4638 . dmGrad - The layout for gradient values 4639 4640 Level: developer 4641 4642 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetGeometryFVM()` 4643 @*/ 4644 PetscErrorCode DMPlexGetGradientDM(DM dm, PetscFV fv, DM *dmGrad) 4645 { 4646 DM plex; 4647 PetscBool computeGradients; 4648 4649 PetscFunctionBegin; 4650 PetscValidHeaderSpecific(dm, DM_CLASSID, 1); 4651 PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2); 4652 PetscAssertPointer(dmGrad, 3); 4653 PetscCall(PetscFVGetComputeGradients(fv, &computeGradients)); 4654 if (!computeGradients) { 4655 *dmGrad = NULL; 4656 PetscFunctionReturn(PETSC_SUCCESS); 4657 } 4658 PetscCall(DMConvertPlex_Internal(dm, &plex, PETSC_TRUE)); 4659 PetscCall(DMPlexGetDataFVM(plex, fv, NULL, NULL, dmGrad)); 4660 PetscCall(DMDestroy(&plex)); 4661 PetscFunctionReturn(PETSC_SUCCESS); 4662 } 4663 4664 static PetscErrorCode DMPlexComputeBdResidual_Single_Internal(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF, DMField coordField, IS facetIS) 4665 { 4666 DM_Plex *mesh = (DM_Plex *)dm->data; 4667 DM plex = NULL, plexA = NULL; 4668 const char *name = "BdResidual"; 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, name, 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 if (mesh->printFEM) { 4790 PetscSection s; 4791 Vec locFbc; 4792 PetscInt pStart, pEnd, maxDof; 4793 PetscScalar *zeroes; 4794 4795 PetscCall(DMGetLocalSection(dm, &s)); 4796 PetscCall(VecDuplicate(locF, &locFbc)); 4797 PetscCall(VecCopy(locF, locFbc)); 4798 PetscCall(PetscSectionGetChart(s, &pStart, &pEnd)); 4799 PetscCall(PetscSectionGetMaxDof(s, &maxDof)); 4800 PetscCall(PetscCalloc1(maxDof, &zeroes)); 4801 for (PetscInt p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, s, p, zeroes, INSERT_BC_VALUES)); 4802 PetscCall(PetscFree(zeroes)); 4803 PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc)); 4804 PetscCall(VecDestroy(&locFbc)); 4805 } 4806 PetscCall(DMDestroy(&plex)); 4807 PetscCall(DMDestroy(&plexA)); 4808 PetscFunctionReturn(PETSC_SUCCESS); 4809 } 4810 4811 PetscErrorCode DMPlexComputeBdResidualSingle(DM dm, PetscReal t, PetscWeakForm wf, PetscFormKey key, Vec locX, Vec locX_t, Vec locF) 4812 { 4813 DMField coordField; 4814 DMLabel depthLabel; 4815 IS facetIS; 4816 PetscInt dim; 4817 4818 PetscFunctionBegin; 4819 PetscCall(DMGetDimension(dm, &dim)); 4820 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 4821 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 4822 PetscCall(DMGetCoordinateField(dm, &coordField)); 4823 PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS)); 4824 PetscCall(ISDestroy(&facetIS)); 4825 PetscFunctionReturn(PETSC_SUCCESS); 4826 } 4827 4828 static PetscErrorCode DMPlexComputeBdResidual_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user) 4829 { 4830 PetscDS prob; 4831 PetscInt numBd, bd; 4832 DMField coordField = NULL; 4833 IS facetIS = NULL; 4834 DMLabel depthLabel; 4835 PetscInt dim; 4836 4837 PetscFunctionBegin; 4838 PetscCall(DMGetDS(dm, &prob)); 4839 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 4840 PetscCall(DMGetDimension(dm, &dim)); 4841 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 4842 PetscCall(PetscDSGetNumBoundary(prob, &numBd)); 4843 for (bd = 0; bd < numBd; ++bd) { 4844 PetscWeakForm wf; 4845 DMBoundaryConditionType type; 4846 DMLabel label; 4847 const PetscInt *values; 4848 PetscInt field, numValues, v; 4849 PetscObject obj; 4850 PetscClassId id; 4851 PetscFormKey key; 4852 4853 PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &field, NULL, NULL, NULL, NULL, NULL)); 4854 if (type & DM_BC_ESSENTIAL) continue; 4855 PetscCall(PetscDSGetDiscretization(prob, field, &obj)); 4856 PetscCall(PetscObjectGetClassId(obj, &id)); 4857 if (id != PETSCFE_CLASSID) continue; 4858 if (!facetIS) { 4859 DMLabel depthLabel; 4860 PetscInt dim; 4861 4862 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 4863 PetscCall(DMGetDimension(dm, &dim)); 4864 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 4865 } 4866 PetscCall(DMGetCoordinateField(dm, &coordField)); 4867 for (v = 0; v < numValues; ++v) { 4868 key.label = label; 4869 key.value = values[v]; 4870 key.field = field; 4871 key.part = 0; 4872 PetscCall(DMPlexComputeBdResidual_Single_Internal(dm, t, wf, key, locX, locX_t, locF, coordField, facetIS)); 4873 } 4874 } 4875 PetscCall(ISDestroy(&facetIS)); 4876 PetscFunctionReturn(PETSC_SUCCESS); 4877 } 4878 4879 PetscErrorCode DMPlexComputeResidual_Internal(DM dm, PetscFormKey key, IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user) 4880 { 4881 DM_Plex *mesh = (DM_Plex *)dm->data; 4882 const char *name = "Residual"; 4883 DM dmAux = NULL; 4884 DM dmGrad = NULL; 4885 DMLabel ghostLabel = NULL; 4886 PetscDS ds = NULL; 4887 PetscDS dsAux = NULL; 4888 PetscSection section = NULL; 4889 PetscBool useFEM = PETSC_FALSE; 4890 PetscBool useFVM = PETSC_FALSE; 4891 PetscBool isImplicit = (locX_t || time == PETSC_MIN_REAL) ? PETSC_TRUE : PETSC_FALSE; 4892 PetscFV fvm = NULL; 4893 DMField coordField = NULL; 4894 Vec locA, cellGeometryFVM = NULL, faceGeometryFVM = NULL, locGrad = NULL; 4895 PetscScalar *u = NULL, *u_t, *a, *uL, *uR; 4896 IS chunkIS; 4897 const PetscInt *cells; 4898 PetscInt cStart, cEnd, numCells; 4899 PetscInt Nf, f, totDim, totDimAux, numChunks, cellChunkSize, faceChunkSize, chunk, fStart, fEnd; 4900 PetscInt maxDegree = PETSC_MAX_INT; 4901 PetscQuadrature affineQuad = NULL, *quads = NULL; 4902 PetscFEGeom *affineGeom = NULL, **geoms = NULL; 4903 4904 PetscFunctionBegin; 4905 PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 4906 if (!cellIS) goto end; 4907 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 4908 if (cStart >= cEnd) goto end; 4909 /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */ 4910 /* TODO The FVM geometry is over-manipulated. Make the precalc functions return exactly what we need */ 4911 /* FEM+FVM */ 4912 PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd)); 4913 /* 1: Get sizes from dm and dmAux */ 4914 PetscCall(DMGetLocalSection(dm, §ion)); 4915 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 4916 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, NULL)); 4917 PetscCall(PetscDSGetNumFields(ds, &Nf)); 4918 PetscCall(PetscDSGetTotalDimension(ds, &totDim)); 4919 PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &locA)); 4920 if (locA) { 4921 PetscInt subcell; 4922 PetscCall(VecGetDM(locA, &dmAux)); 4923 PetscCall(DMGetEnclosurePoint(dmAux, dm, DM_ENC_UNKNOWN, cells ? cells[cStart] : cStart, &subcell)); 4924 PetscCall(DMGetCellDS(dmAux, subcell, &dsAux, NULL)); 4925 PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux)); 4926 } 4927 /* 2: Get geometric data */ 4928 for (f = 0; f < Nf; ++f) { 4929 PetscObject obj; 4930 PetscClassId id; 4931 PetscBool fimp; 4932 4933 PetscCall(PetscDSGetImplicit(ds, f, &fimp)); 4934 if (isImplicit != fimp) continue; 4935 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 4936 PetscCall(PetscObjectGetClassId(obj, &id)); 4937 if (id == PETSCFE_CLASSID) useFEM = PETSC_TRUE; 4938 if (id == PETSCFV_CLASSID) { 4939 useFVM = PETSC_TRUE; 4940 fvm = (PetscFV)obj; 4941 } 4942 } 4943 if (useFEM) { 4944 PetscCall(DMGetCoordinateField(dm, &coordField)); 4945 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 4946 if (maxDegree <= 1) { 4947 PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &affineQuad)); 4948 if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 4949 } else { 4950 PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms)); 4951 for (f = 0; f < Nf; ++f) { 4952 PetscObject obj; 4953 PetscClassId id; 4954 PetscBool fimp; 4955 4956 PetscCall(PetscDSGetImplicit(ds, f, &fimp)); 4957 if (isImplicit != fimp) continue; 4958 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 4959 PetscCall(PetscObjectGetClassId(obj, &id)); 4960 if (id == PETSCFE_CLASSID) { 4961 PetscFE fe = (PetscFE)obj; 4962 4963 PetscCall(PetscFEGetQuadrature(fe, &quads[f])); 4964 PetscCall(PetscObjectReference((PetscObject)quads[f])); 4965 PetscCall(DMSNESGetFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 4966 } 4967 } 4968 } 4969 } 4970 // Handle non-essential (e.g. outflow) boundary values 4971 if (useFVM) { 4972 PetscCall(DMPlexInsertBoundaryValuesFVM(dm, fvm, locX, time, &locGrad)); 4973 PetscCall(DMPlexGetGeometryFVM(dm, &faceGeometryFVM, &cellGeometryFVM, NULL)); 4974 PetscCall(DMPlexGetGradientDM(dm, fvm, &dmGrad)); 4975 } 4976 /* Loop over chunks */ 4977 if (useFEM) PetscCall(ISCreate(PETSC_COMM_SELF, &chunkIS)); 4978 numCells = cEnd - cStart; 4979 numChunks = 1; 4980 cellChunkSize = numCells / numChunks; 4981 faceChunkSize = (fEnd - fStart) / numChunks; 4982 numChunks = PetscMin(1, numCells); 4983 for (chunk = 0; chunk < numChunks; ++chunk) { 4984 PetscScalar *elemVec, *fluxL, *fluxR; 4985 PetscReal *vol; 4986 PetscFVFaceGeom *fgeom; 4987 PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c; 4988 PetscInt fS = fStart + chunk * faceChunkSize, fE = PetscMin(fS + faceChunkSize, fEnd), numFaces = 0, face; 4989 4990 /* Extract field coefficients */ 4991 if (useFEM) { 4992 PetscCall(ISGetPointSubrange(chunkIS, cS, cE, cells)); 4993 PetscCall(DMPlexGetCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a)); 4994 PetscCall(DMGetWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec)); 4995 PetscCall(PetscArrayzero(elemVec, numCells * totDim)); 4996 } 4997 if (useFVM) { 4998 PetscCall(DMPlexGetFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR)); 4999 PetscCall(DMPlexGetFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol)); 5000 PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL)); 5001 PetscCall(DMGetWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR)); 5002 PetscCall(PetscArrayzero(fluxL, numFaces * totDim)); 5003 PetscCall(PetscArrayzero(fluxR, numFaces * totDim)); 5004 } 5005 /* TODO We will interlace both our field coefficients (u, u_t, uL, uR, etc.) and our output (elemVec, fL, fR). I think this works */ 5006 /* Loop over fields */ 5007 for (f = 0; f < Nf; ++f) { 5008 PetscObject obj; 5009 PetscClassId id; 5010 PetscBool fimp; 5011 PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset; 5012 5013 key.field = f; 5014 PetscCall(PetscDSGetImplicit(ds, f, &fimp)); 5015 if (isImplicit != fimp) continue; 5016 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 5017 PetscCall(PetscObjectGetClassId(obj, &id)); 5018 if (id == PETSCFE_CLASSID) { 5019 PetscFE fe = (PetscFE)obj; 5020 PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f]; 5021 PetscFEGeom *chunkGeom = NULL; 5022 PetscQuadrature quad = affineQuad ? affineQuad : quads[f]; 5023 PetscInt Nq, Nb; 5024 5025 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 5026 PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL)); 5027 PetscCall(PetscFEGetDimension(fe, &Nb)); 5028 blockSize = Nb; 5029 batchSize = numBlocks * blockSize; 5030 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 5031 numChunks = numCells / (numBatches * batchSize); 5032 Ne = numChunks * numBatches * batchSize; 5033 Nr = numCells % (numBatches * batchSize); 5034 offset = numCells - Nr; 5035 /* Integrate FE residual to get elemVec (need fields at quadrature points) */ 5036 /* 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) */ 5037 PetscCall(PetscFEGeomGetChunk(geom, 0, offset, &chunkGeom)); 5038 PetscCall(PetscFEIntegrateResidual(ds, key, Ne, chunkGeom, u, u_t, dsAux, a, t, elemVec)); 5039 PetscCall(PetscFEGeomGetChunk(geom, offset, numCells, &chunkGeom)); 5040 PetscCall(PetscFEIntegrateResidual(ds, key, Nr, chunkGeom, &u[offset * totDim], PetscSafePointerPlusOffset(u_t, offset * totDim), dsAux, PetscSafePointerPlusOffset(a, offset * totDimAux), t, &elemVec[offset * totDim])); 5041 PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &chunkGeom)); 5042 } else if (id == PETSCFV_CLASSID) { 5043 PetscFV fv = (PetscFV)obj; 5044 5045 Ne = numFaces; 5046 /* Riemann solve over faces (need fields at face centroids) */ 5047 /* We need to evaluate FE fields at those coordinates */ 5048 PetscCall(PetscFVIntegrateRHSFunction(fv, ds, f, Ne, fgeom, vol, uL, uR, fluxL, fluxR)); 5049 } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Unknown discretization type for field %" PetscInt_FMT, f); 5050 } 5051 /* Loop over domain */ 5052 if (useFEM) { 5053 /* Add elemVec to locX */ 5054 for (c = cS; c < cE; ++c) { 5055 const PetscInt cell = cells ? cells[c] : c; 5056 const PetscInt cind = c - cStart; 5057 5058 if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVec[cind * totDim])); 5059 if (ghostLabel) { 5060 PetscInt ghostVal; 5061 5062 PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal)); 5063 if (ghostVal > 0) continue; 5064 } 5065 PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVec[cind * totDim], ADD_ALL_VALUES)); 5066 } 5067 } 5068 if (useFVM) { 5069 PetscScalar *fa; 5070 PetscInt iface; 5071 5072 PetscCall(VecGetArray(locF, &fa)); 5073 for (f = 0; f < Nf; ++f) { 5074 PetscFV fv; 5075 PetscObject obj; 5076 PetscClassId id; 5077 PetscInt cdim, foff, pdim; 5078 5079 PetscCall(DMGetCoordinateDim(dm, &cdim)); 5080 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 5081 PetscCall(PetscDSGetFieldOffset(ds, f, &foff)); 5082 PetscCall(PetscObjectGetClassId(obj, &id)); 5083 if (id != PETSCFV_CLASSID) continue; 5084 fv = (PetscFV)obj; 5085 PetscCall(PetscFVGetNumComponents(fv, &pdim)); 5086 /* Accumulate fluxes to cells */ 5087 for (face = fS, iface = 0; face < fE; ++face) { 5088 const PetscInt *scells; 5089 PetscScalar *fL = NULL, *fR = NULL; 5090 PetscInt ghost, d, nsupp, nchild; 5091 5092 PetscCall(DMLabelGetValue(ghostLabel, face, &ghost)); 5093 PetscCall(DMPlexGetSupportSize(dm, face, &nsupp)); 5094 PetscCall(DMPlexGetTreeChildren(dm, face, &nchild, NULL)); 5095 if (ghost >= 0 || nsupp > 2 || nchild > 0) continue; 5096 PetscCall(DMPlexGetSupport(dm, face, &scells)); 5097 PetscCall(DMLabelGetValue(ghostLabel, scells[0], &ghost)); 5098 if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[0], f, fa, &fL)); 5099 PetscCall(DMLabelGetValue(ghostLabel, scells[1], &ghost)); 5100 if (ghost <= 0) PetscCall(DMPlexPointLocalFieldRef(dm, scells[1], f, fa, &fR)); 5101 if (mesh->printFVM > 1) { 5102 PetscCall(DMPrintCellVectorReal(face, "Residual: normal", cdim, fgeom[iface].normal)); 5103 PetscCall(DMPrintCellVector(face, "Residual: left state", pdim, &uL[iface * totDim + foff])); 5104 PetscCall(DMPrintCellVector(face, "Residual: right state", pdim, &uR[iface * totDim + foff])); 5105 PetscCall(DMPrintCellVector(face, "Residual: left flux", pdim, &fluxL[iface * totDim + foff])); 5106 PetscCall(DMPrintCellVector(face, "Residual: right flux", pdim, &fluxR[iface * totDim + foff])); 5107 } 5108 for (d = 0; d < pdim; ++d) { 5109 if (fL) fL[d] -= fluxL[iface * totDim + foff + d]; 5110 if (fR) fR[d] += fluxR[iface * totDim + foff + d]; 5111 } 5112 ++iface; 5113 } 5114 } 5115 PetscCall(VecRestoreArray(locF, &fa)); 5116 } 5117 /* Handle time derivative */ 5118 if (locX_t) { 5119 PetscScalar *x_t, *fa; 5120 5121 PetscCall(VecGetArray(locF, &fa)); 5122 PetscCall(VecGetArray(locX_t, &x_t)); 5123 for (f = 0; f < Nf; ++f) { 5124 PetscFV fv; 5125 PetscObject obj; 5126 PetscClassId id; 5127 PetscInt pdim, d; 5128 5129 PetscCall(PetscDSGetDiscretization(ds, f, &obj)); 5130 PetscCall(PetscObjectGetClassId(obj, &id)); 5131 if (id != PETSCFV_CLASSID) continue; 5132 fv = (PetscFV)obj; 5133 PetscCall(PetscFVGetNumComponents(fv, &pdim)); 5134 for (c = cS; c < cE; ++c) { 5135 const PetscInt cell = cells ? cells[c] : c; 5136 PetscScalar *u_t, *r; 5137 5138 if (ghostLabel) { 5139 PetscInt ghostVal; 5140 5141 PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal)); 5142 if (ghostVal > 0) continue; 5143 } 5144 PetscCall(DMPlexPointLocalFieldRead(dm, cell, f, x_t, &u_t)); 5145 PetscCall(DMPlexPointLocalFieldRef(dm, cell, f, fa, &r)); 5146 for (d = 0; d < pdim; ++d) r[d] += u_t[d]; 5147 } 5148 } 5149 PetscCall(VecRestoreArray(locX_t, &x_t)); 5150 PetscCall(VecRestoreArray(locF, &fa)); 5151 } 5152 if (useFEM) { 5153 PetscCall(DMPlexRestoreCellFields(dm, chunkIS, locX, locX_t, locA, &u, &u_t, &a)); 5154 PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVec)); 5155 } 5156 if (useFVM) { 5157 PetscCall(DMPlexRestoreFaceFields(dm, fS, fE, locX, locX_t, faceGeometryFVM, cellGeometryFVM, locGrad, &numFaces, &uL, &uR)); 5158 PetscCall(DMPlexRestoreFaceGeometry(dm, fS, fE, faceGeometryFVM, cellGeometryFVM, &numFaces, &fgeom, &vol)); 5159 PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxL)); 5160 PetscCall(DMRestoreWorkArray(dm, numFaces * totDim, MPIU_SCALAR, &fluxR)); 5161 if (dmGrad) PetscCall(DMRestoreLocalVector(dmGrad, &locGrad)); 5162 } 5163 } 5164 if (useFEM) PetscCall(ISDestroy(&chunkIS)); 5165 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 5166 5167 if (useFEM) { 5168 PetscCall(DMPlexComputeBdResidual_Internal(dm, locX, locX_t, t, locF, user)); 5169 5170 if (maxDegree <= 1) { 5171 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 5172 PetscCall(PetscQuadratureDestroy(&affineQuad)); 5173 } else { 5174 for (f = 0; f < Nf; ++f) { 5175 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 5176 PetscCall(PetscQuadratureDestroy(&quads[f])); 5177 } 5178 PetscCall(PetscFree2(quads, geoms)); 5179 } 5180 } 5181 5182 /* FEM */ 5183 /* 1: Get sizes from dm and dmAux */ 5184 /* 2: Get geometric data */ 5185 /* 3: Handle boundary values */ 5186 /* 4: Loop over domain */ 5187 /* Extract coefficients */ 5188 /* Loop over fields */ 5189 /* Set tiling for FE*/ 5190 /* Integrate FE residual to get elemVec */ 5191 /* Loop over subdomain */ 5192 /* Loop over quad points */ 5193 /* Transform coords to real space */ 5194 /* Evaluate field and aux fields at point */ 5195 /* Evaluate residual at point */ 5196 /* Transform residual to real space */ 5197 /* Add residual to elemVec */ 5198 /* Loop over domain */ 5199 /* Add elemVec to locX */ 5200 5201 /* FVM */ 5202 /* Get geometric data */ 5203 /* If using gradients */ 5204 /* Compute gradient data */ 5205 /* Loop over domain faces */ 5206 /* Count computational faces */ 5207 /* Reconstruct cell gradient */ 5208 /* Loop over domain cells */ 5209 /* Limit cell gradients */ 5210 /* Handle boundary values */ 5211 /* Loop over domain faces */ 5212 /* Read out field, centroid, normal, volume for each side of face */ 5213 /* Riemann solve over faces */ 5214 /* Loop over domain faces */ 5215 /* Accumulate fluxes to cells */ 5216 /* TODO Change printFEM to printDisc here */ 5217 if (mesh->printFEM) { 5218 Vec locFbc; 5219 PetscInt pStart, pEnd, p, maxDof; 5220 PetscScalar *zeroes; 5221 5222 PetscCall(VecDuplicate(locF, &locFbc)); 5223 PetscCall(VecCopy(locF, locFbc)); 5224 PetscCall(PetscSectionGetChart(section, &pStart, &pEnd)); 5225 PetscCall(PetscSectionGetMaxDof(section, &maxDof)); 5226 PetscCall(PetscCalloc1(maxDof, &zeroes)); 5227 for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES)); 5228 PetscCall(PetscFree(zeroes)); 5229 PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc)); 5230 PetscCall(VecDestroy(&locFbc)); 5231 } 5232 end: 5233 PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 5234 PetscFunctionReturn(PETSC_SUCCESS); 5235 } 5236 5237 /* 5238 1) Allow multiple kernels for BdResidual for hybrid DS 5239 5240 DONE 2) Get out dsAux for either side at the same time as cohesive cell dsAux 5241 5242 DONE 3) Change DMGetCellFields() to get different aux data a[] for each side 5243 - I think I just need to replace a[] with the closure from each face 5244 5245 4) Run both kernels for each non-hybrid field with correct dsAux, and then hybrid field as before 5246 */ 5247 PetscErrorCode DMPlexComputeResidual_Hybrid_Internal(DM dm, PetscFormKey key[], IS cellIS, PetscReal time, Vec locX, Vec locX_t, PetscReal t, Vec locF, void *user) 5248 { 5249 DM_Plex *mesh = (DM_Plex *)dm->data; 5250 const char *name = "Hybrid Residual"; 5251 DM dmAux[3] = {NULL, NULL, NULL}; 5252 DMLabel ghostLabel = NULL; 5253 PetscDS ds = NULL; 5254 PetscDS dsIn = NULL; 5255 PetscDS dsAux[3] = {NULL, NULL, NULL}; 5256 Vec locA[3] = {NULL, NULL, NULL}; 5257 DM dmScale[3] = {NULL, NULL, NULL}; 5258 PetscDS dsScale[3] = {NULL, NULL, NULL}; 5259 Vec locS[3] = {NULL, NULL, NULL}; 5260 PetscSection section = NULL; 5261 DMField coordField = NULL; 5262 PetscScalar *a[3] = {NULL, NULL, NULL}; 5263 PetscScalar *s[3] = {NULL, NULL, NULL}; 5264 PetscScalar *u = NULL, *u_t; 5265 PetscScalar *elemVecNeg, *elemVecPos, *elemVecCoh; 5266 IS chunkIS; 5267 const PetscInt *cells; 5268 PetscInt *faces; 5269 PetscInt cStart, cEnd, numCells; 5270 PetscInt Nf, f, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk; 5271 PetscInt maxDegree = PETSC_MAX_INT; 5272 PetscQuadrature affineQuad = NULL, *quads = NULL; 5273 PetscFEGeom *affineGeom = NULL, **geoms = NULL; 5274 5275 PetscFunctionBegin; 5276 PetscCall(PetscLogEventBegin(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 5277 if (!cellIS) goto end; 5278 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 5279 PetscCall(ISGetLocalSize(cellIS, &numCells)); 5280 if (cStart >= cEnd) goto end; 5281 if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) { 5282 const char *name; 5283 PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name)); 5284 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); 5285 } 5286 /* TODO The places where we have to use isFE are probably the member functions for the PetscDisc class */ 5287 /* FEM */ 5288 /* 1: Get sizes from dm and dmAux */ 5289 PetscCall(DMGetSection(dm, §ion)); 5290 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 5291 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn)); 5292 PetscCall(PetscDSGetNumFields(ds, &Nf)); 5293 PetscCall(PetscDSGetTotalDimension(ds, &totDim)); 5294 PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn)); 5295 PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2])); 5296 if (locA[2]) { 5297 const PetscInt cellStart = cells ? cells[cStart] : cStart; 5298 5299 PetscCall(VecGetDM(locA[2], &dmAux[2])); 5300 PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL)); 5301 PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2])); 5302 { 5303 const PetscInt *cone; 5304 PetscInt c; 5305 5306 PetscCall(DMPlexGetCone(dm, cellStart, &cone)); 5307 for (c = 0; c < 2; ++c) { 5308 const PetscInt *support; 5309 PetscInt ssize, s; 5310 5311 PetscCall(DMPlexGetSupport(dm, cone[c], &support)); 5312 PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize)); 5313 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); 5314 if (support[0] == cellStart) s = 1; 5315 else if (support[1] == cellStart) s = 0; 5316 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart); 5317 PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c])); 5318 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); 5319 if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c])); 5320 else dmAux[c] = dmAux[2]; 5321 PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL)); 5322 PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c])); 5323 } 5324 } 5325 } 5326 /* Handle mass matrix scaling 5327 The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */ 5328 PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2])); 5329 if (locS[2]) { 5330 const PetscInt cellStart = cells ? cells[cStart] : cStart; 5331 PetscInt Nb, Nbs; 5332 5333 PetscCall(VecGetDM(locS[2], &dmScale[2])); 5334 PetscCall(DMGetCellDS(dmScale[2], cellStart, &dsScale[2], NULL)); 5335 PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2])); 5336 // BRAD: This is not set correctly 5337 key[2].field = 2; 5338 PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb)); 5339 PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs)); 5340 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); 5341 { 5342 const PetscInt *cone; 5343 PetscInt c; 5344 5345 locS[1] = locS[0] = locS[2]; 5346 dmScale[1] = dmScale[0] = dmScale[2]; 5347 PetscCall(DMPlexGetCone(dm, cellStart, &cone)); 5348 for (c = 0; c < 2; ++c) { 5349 const PetscInt *support; 5350 PetscInt ssize, s; 5351 5352 PetscCall(DMPlexGetSupport(dm, cone[c], &support)); 5353 PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize)); 5354 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); 5355 if (support[0] == cellStart) s = 1; 5356 else if (support[1] == cellStart) s = 0; 5357 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart); 5358 PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL)); 5359 PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c])); 5360 } 5361 } 5362 } 5363 /* 2: Setup geometric data */ 5364 PetscCall(DMGetCoordinateField(dm, &coordField)); 5365 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 5366 if (maxDegree > 1) { 5367 PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms)); 5368 for (f = 0; f < Nf; ++f) { 5369 PetscFE fe; 5370 5371 PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe)); 5372 if (fe) { 5373 PetscCall(PetscFEGetQuadrature(fe, &quads[f])); 5374 PetscCall(PetscObjectReference((PetscObject)quads[f])); 5375 } 5376 } 5377 } 5378 /* Loop over chunks */ 5379 cellChunkSize = numCells; 5380 numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize); 5381 PetscCall(PetscCalloc1(2 * cellChunkSize, &faces)); 5382 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 2 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS)); 5383 /* Extract field coefficients */ 5384 /* NOTE This needs the end cap faces to have identical orientations */ 5385 PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2])); 5386 PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a)); 5387 PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s)); 5388 PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecNeg)); 5389 PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecPos)); 5390 PetscCall(DMGetWorkArray(dm, cellChunkSize * totDim, MPIU_SCALAR, &elemVecCoh)); 5391 for (chunk = 0; chunk < numChunks; ++chunk) { 5392 PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c; 5393 5394 PetscCall(PetscArrayzero(elemVecNeg, cellChunkSize * totDim)); 5395 PetscCall(PetscArrayzero(elemVecPos, cellChunkSize * totDim)); 5396 PetscCall(PetscArrayzero(elemVecCoh, cellChunkSize * totDim)); 5397 /* Get faces */ 5398 for (c = cS; c < cE; ++c) { 5399 const PetscInt cell = cells ? cells[c] : c; 5400 const PetscInt *cone; 5401 PetscCall(DMPlexGetCone(dm, cell, &cone)); 5402 faces[(c - cS) * 2 + 0] = cone[0]; 5403 faces[(c - cS) * 2 + 1] = cone[1]; 5404 } 5405 PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER)); 5406 /* Get geometric data */ 5407 if (maxDegree <= 1) { 5408 if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad)); 5409 if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom)); 5410 } else { 5411 for (f = 0; f < Nf; ++f) { 5412 if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f])); 5413 } 5414 } 5415 /* Loop over fields */ 5416 for (f = 0; f < Nf; ++f) { 5417 PetscFE fe; 5418 PetscFEGeom *geom = affineGeom ? affineGeom : geoms[f]; 5419 PetscFEGeom *chunkGeom = NULL, *remGeom = NULL; 5420 PetscQuadrature quad = affineQuad ? affineQuad : quads[f]; 5421 PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb; 5422 PetscBool isCohesiveField; 5423 5424 PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe)); 5425 if (!fe) continue; 5426 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 5427 PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL)); 5428 PetscCall(PetscFEGetDimension(fe, &Nb)); 5429 blockSize = Nb; 5430 batchSize = numBlocks * blockSize; 5431 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 5432 numChunks = numCells / (numBatches * batchSize); 5433 Ne = numChunks * numBatches * batchSize; 5434 Nr = numCells % (numBatches * batchSize); 5435 offset = numCells - Nr; 5436 PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom)); 5437 PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom)); 5438 PetscCall(PetscDSGetCohesive(ds, f, &isCohesiveField)); 5439 chunkGeom->isCohesive = remGeom->isCohesive = PETSC_TRUE; 5440 key[0].field = f; 5441 key[1].field = f; 5442 key[2].field = f; 5443 PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, elemVecNeg)); 5444 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])); 5445 PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, elemVecPos)); 5446 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])); 5447 PetscCall(PetscFEIntegrateHybridResidual(ds, dsIn, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, elemVecCoh)); 5448 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])); 5449 PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom)); 5450 PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom)); 5451 } 5452 /* Add elemVec to locX */ 5453 for (c = cS; c < cE; ++c) { 5454 const PetscInt cell = cells ? cells[c] : c; 5455 const PetscInt cind = c - cStart; 5456 PetscInt i; 5457 5458 /* Scale element values */ 5459 if (locS[0]) { 5460 PetscInt Nb, off = cind * totDim, soff = cind * totDimScale[0]; 5461 PetscBool cohesive; 5462 5463 for (f = 0; f < Nf; ++f) { 5464 PetscCall(PetscDSGetFieldSize(ds, f, &Nb)); 5465 PetscCall(PetscDSGetCohesive(ds, f, &cohesive)); 5466 if (f == key[2].field) { 5467 PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields"); 5468 // No cohesive scaling field is currently input 5469 for (i = 0; i < Nb; ++i) elemVecCoh[off + i] += s[0][soff + i] * elemVecNeg[off + i] + s[1][soff + i] * elemVecPos[off + i]; 5470 off += Nb; 5471 } else { 5472 const PetscInt N = cohesive ? Nb : Nb * 2; 5473 5474 for (i = 0; i < N; ++i) elemVecCoh[off + i] += elemVecNeg[off + i] + elemVecPos[off + i]; 5475 off += N; 5476 } 5477 } 5478 } else { 5479 for (i = cind * totDim; i < (cind + 1) * totDim; ++i) elemVecCoh[i] += elemVecNeg[i] + elemVecPos[i]; 5480 } 5481 if (mesh->printFEM > 1) PetscCall(DMPrintCellVector(cell, name, totDim, &elemVecCoh[cind * totDim])); 5482 if (ghostLabel) { 5483 PetscInt ghostVal; 5484 5485 PetscCall(DMLabelGetValue(ghostLabel, cell, &ghostVal)); 5486 if (ghostVal > 0) continue; 5487 } 5488 PetscCall(DMPlexVecSetClosure(dm, section, locF, cell, &elemVecCoh[cind * totDim], ADD_ALL_VALUES)); 5489 } 5490 } 5491 PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2])); 5492 PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a)); 5493 PetscCall(DMPlexRestoreHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s)); 5494 PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecNeg)); 5495 PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecPos)); 5496 PetscCall(DMRestoreWorkArray(dm, numCells * totDim, MPIU_SCALAR, &elemVecCoh)); 5497 PetscCall(PetscFree(faces)); 5498 PetscCall(ISDestroy(&chunkIS)); 5499 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 5500 if (maxDegree <= 1) { 5501 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 5502 PetscCall(PetscQuadratureDestroy(&affineQuad)); 5503 } else { 5504 for (f = 0; f < Nf; ++f) { 5505 if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 5506 if (quads) PetscCall(PetscQuadratureDestroy(&quads[f])); 5507 } 5508 PetscCall(PetscFree2(quads, geoms)); 5509 } 5510 if (mesh->printFEM) { 5511 Vec locFbc; 5512 PetscInt pStart, pEnd, p, maxDof; 5513 PetscScalar *zeroes; 5514 5515 PetscCall(VecDuplicate(locF, &locFbc)); 5516 PetscCall(VecCopy(locF, locFbc)); 5517 PetscCall(PetscSectionGetChart(section, &pStart, &pEnd)); 5518 PetscCall(PetscSectionGetMaxDof(section, &maxDof)); 5519 PetscCall(PetscCalloc1(maxDof, &zeroes)); 5520 for (p = pStart; p < pEnd; p++) PetscCall(VecSetValuesSection(locFbc, section, p, zeroes, INSERT_BC_VALUES)); 5521 PetscCall(PetscFree(zeroes)); 5522 PetscCall(DMPrintLocalVec(dm, name, mesh->printTol, locFbc)); 5523 PetscCall(VecDestroy(&locFbc)); 5524 } 5525 end: 5526 PetscCall(PetscLogEventEnd(DMPLEX_ResidualFEM, dm, 0, 0, 0)); 5527 PetscFunctionReturn(PETSC_SUCCESS); 5528 } 5529 5530 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) 5531 { 5532 DM_Plex *mesh = (DM_Plex *)dm->data; 5533 DM plex = NULL, plexA = NULL, tdm; 5534 DMEnclosureType encAux; 5535 PetscDS ds, dsAux = NULL; 5536 PetscSection section, sectionAux = NULL; 5537 PetscSection globalSection; 5538 Vec locA = NULL, tv; 5539 PetscScalar *u = NULL, *u_t = NULL, *a = NULL, *elemMat = NULL, *elemMatP = NULL; 5540 PetscInt v; 5541 PetscInt Nf, totDim, totDimAux = 0; 5542 PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, transform; 5543 5544 PetscFunctionBegin; 5545 PetscCall(DMHasBasisTransform(dm, &transform)); 5546 PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm)); 5547 PetscCall(DMGetBasisTransformVec_Internal(dm, &tv)); 5548 PetscCall(DMGetLocalSection(dm, §ion)); 5549 PetscCall(DMGetDS(dm, &ds)); 5550 PetscCall(PetscDSGetNumFields(ds, &Nf)); 5551 PetscCall(PetscDSGetTotalDimension(ds, &totDim)); 5552 PetscCall(PetscWeakFormHasBdJacobian(wf, &hasJac)); 5553 PetscCall(PetscWeakFormHasBdJacobianPreconditioner(wf, &hasPrec)); 5554 if (!hasJac && !hasPrec) PetscFunctionReturn(PETSC_SUCCESS); 5555 PetscCall(DMConvert(dm, DMPLEX, &plex)); 5556 PetscCall(DMGetAuxiliaryVec(dm, label, values[0], 0, &locA)); 5557 if (locA) { 5558 DM dmAux; 5559 5560 PetscCall(VecGetDM(locA, &dmAux)); 5561 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 5562 PetscCall(DMConvert(dmAux, DMPLEX, &plexA)); 5563 PetscCall(DMGetDS(plexA, &dsAux)); 5564 PetscCall(PetscDSGetTotalDimension(dsAux, &totDimAux)); 5565 PetscCall(DMGetLocalSection(plexA, §ionAux)); 5566 } 5567 5568 PetscCall(DMGetGlobalSection(dm, &globalSection)); 5569 for (v = 0; v < numValues; ++v) { 5570 PetscFEGeom *fgeom; 5571 PetscInt maxDegree; 5572 PetscQuadrature qGeom = NULL; 5573 IS pointIS; 5574 const PetscInt *points; 5575 PetscFormKey key; 5576 PetscInt numFaces, face, Nq; 5577 5578 key.label = label; 5579 key.value = values[v]; 5580 key.part = 0; 5581 PetscCall(DMLabelGetStratumIS(label, values[v], &pointIS)); 5582 if (!pointIS) continue; /* No points with that id on this process */ 5583 { 5584 IS isectIS; 5585 5586 /* TODO: Special cases of ISIntersect where it is quick to check a prior if one is a superset of the other */ 5587 PetscCall(ISIntersect_Caching_Internal(facetIS, pointIS, &isectIS)); 5588 PetscCall(ISDestroy(&pointIS)); 5589 pointIS = isectIS; 5590 } 5591 PetscCall(ISGetLocalSize(pointIS, &numFaces)); 5592 PetscCall(ISGetIndices(pointIS, &points)); 5593 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)); 5594 PetscCall(DMFieldGetDegree(coordField, pointIS, NULL, &maxDegree)); 5595 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, pointIS, &qGeom)); 5596 if (!qGeom) { 5597 PetscFE fe; 5598 5599 PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe)); 5600 PetscCall(PetscFEGetFaceQuadrature(fe, &qGeom)); 5601 PetscCall(PetscObjectReference((PetscObject)qGeom)); 5602 } 5603 PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL)); 5604 PetscCall(DMSNESGetFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom)); 5605 for (face = 0; face < numFaces; ++face) { 5606 const PetscInt point = points[face], *support; 5607 PetscScalar *x = NULL; 5608 PetscInt i; 5609 5610 PetscCall(DMPlexGetSupport(dm, point, &support)); 5611 PetscCall(DMPlexVecGetClosure(plex, section, locX, support[0], NULL, &x)); 5612 for (i = 0; i < totDim; ++i) u[face * totDim + i] = x[i]; 5613 PetscCall(DMPlexVecRestoreClosure(plex, section, locX, support[0], NULL, &x)); 5614 if (locX_t) { 5615 PetscCall(DMPlexVecGetClosure(plex, section, locX_t, support[0], NULL, &x)); 5616 for (i = 0; i < totDim; ++i) u_t[face * totDim + i] = x[i]; 5617 PetscCall(DMPlexVecRestoreClosure(plex, section, locX_t, support[0], NULL, &x)); 5618 } 5619 if (locA) { 5620 PetscInt subp; 5621 PetscCall(DMGetEnclosurePoint(plexA, dm, encAux, support[0], &subp)); 5622 PetscCall(DMPlexVecGetClosure(plexA, sectionAux, locA, subp, NULL, &x)); 5623 for (i = 0; i < totDimAux; ++i) a[face * totDimAux + i] = x[i]; 5624 PetscCall(DMPlexVecRestoreClosure(plexA, sectionAux, locA, subp, NULL, &x)); 5625 } 5626 } 5627 if (elemMat) PetscCall(PetscArrayzero(elemMat, numFaces * totDim * totDim)); 5628 if (elemMatP) PetscCall(PetscArrayzero(elemMatP, numFaces * totDim * totDim)); 5629 { 5630 PetscFE fe; 5631 PetscInt Nb; 5632 /* Conforming batches */ 5633 PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize; 5634 /* Remainder */ 5635 PetscFEGeom *chunkGeom = NULL; 5636 PetscInt fieldJ, Nr, offset; 5637 5638 PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&fe)); 5639 PetscCall(PetscFEGetDimension(fe, &Nb)); 5640 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 5641 blockSize = Nb; 5642 batchSize = numBlocks * blockSize; 5643 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 5644 numChunks = numFaces / (numBatches * batchSize); 5645 Ne = numChunks * numBatches * batchSize; 5646 Nr = numFaces % (numBatches * batchSize); 5647 offset = numFaces - Nr; 5648 PetscCall(PetscFEGeomGetChunk(fgeom, 0, offset, &chunkGeom)); 5649 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 5650 key.field = fieldI * Nf + fieldJ; 5651 if (hasJac) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMat)); 5652 if (hasPrec) PetscCall(PetscFEIntegrateBdJacobian(ds, wf, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, dsAux, a, t, X_tShift, elemMatP)); 5653 } 5654 PetscCall(PetscFEGeomGetChunk(fgeom, offset, numFaces, &chunkGeom)); 5655 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 5656 key.field = fieldI * Nf + fieldJ; 5657 if (hasJac) 5658 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])); 5659 if (hasPrec) 5660 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])); 5661 } 5662 PetscCall(PetscFEGeomRestoreChunk(fgeom, offset, numFaces, &chunkGeom)); 5663 } 5664 for (face = 0; face < numFaces; ++face) { 5665 const PetscInt point = points[face], *support; 5666 5667 /* Transform to global basis before insertion in Jacobian */ 5668 PetscCall(DMPlexGetSupport(plex, point, &support)); 5669 if (hasJac && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMat[face * totDim * totDim])); 5670 if (hasPrec && transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, support[0], PETSC_TRUE, totDim, &elemMatP[face * totDim * totDim])); 5671 if (hasPrec) { 5672 if (hasJac) { 5673 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim])); 5674 PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES)); 5675 } 5676 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMatP[face * totDim * totDim])); 5677 PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, support[0], &elemMatP[face * totDim * totDim], ADD_VALUES)); 5678 } else { 5679 if (hasJac) { 5680 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(point, "BdJacobian", totDim, totDim, &elemMat[face * totDim * totDim])); 5681 PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, support[0], &elemMat[face * totDim * totDim], ADD_VALUES)); 5682 } 5683 } 5684 } 5685 PetscCall(DMSNESRestoreFEGeom(coordField, pointIS, qGeom, PETSC_TRUE, &fgeom)); 5686 PetscCall(PetscQuadratureDestroy(&qGeom)); 5687 PetscCall(ISRestoreIndices(pointIS, &points)); 5688 PetscCall(ISDestroy(&pointIS)); 5689 PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, a)); 5690 } 5691 if (plex) PetscCall(DMDestroy(&plex)); 5692 if (plexA) PetscCall(DMDestroy(&plexA)); 5693 PetscFunctionReturn(PETSC_SUCCESS); 5694 } 5695 5696 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) 5697 { 5698 DMField coordField; 5699 DMLabel depthLabel; 5700 IS facetIS; 5701 PetscInt dim; 5702 5703 PetscFunctionBegin; 5704 PetscCall(DMGetDimension(dm, &dim)); 5705 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 5706 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 5707 PetscCall(DMGetCoordinateField(dm, &coordField)); 5708 PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, field, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS)); 5709 PetscCall(ISDestroy(&facetIS)); 5710 PetscFunctionReturn(PETSC_SUCCESS); 5711 } 5712 5713 static PetscErrorCode DMPlexComputeBdJacobian_Internal(DM dm, Vec locX, Vec locX_t, PetscReal t, PetscReal X_tShift, Mat Jac, Mat JacP, void *user) 5714 { 5715 PetscDS prob; 5716 PetscInt dim, numBd, bd; 5717 DMLabel depthLabel; 5718 DMField coordField = NULL; 5719 IS facetIS; 5720 5721 PetscFunctionBegin; 5722 PetscCall(DMGetDS(dm, &prob)); 5723 PetscCall(DMPlexGetDepthLabel(dm, &depthLabel)); 5724 PetscCall(DMGetDimension(dm, &dim)); 5725 PetscCall(DMLabelGetStratumIS(depthLabel, dim - 1, &facetIS)); 5726 PetscCall(PetscDSGetNumBoundary(prob, &numBd)); 5727 PetscCall(DMGetCoordinateField(dm, &coordField)); 5728 for (bd = 0; bd < numBd; ++bd) { 5729 PetscWeakForm wf; 5730 DMBoundaryConditionType type; 5731 DMLabel label; 5732 const PetscInt *values; 5733 PetscInt fieldI, numValues; 5734 PetscObject obj; 5735 PetscClassId id; 5736 5737 PetscCall(PetscDSGetBoundary(prob, bd, &wf, &type, NULL, &label, &numValues, &values, &fieldI, NULL, NULL, NULL, NULL, NULL)); 5738 if (type & DM_BC_ESSENTIAL) continue; 5739 PetscCall(PetscDSGetDiscretization(prob, fieldI, &obj)); 5740 PetscCall(PetscObjectGetClassId(obj, &id)); 5741 if (id != PETSCFE_CLASSID) continue; 5742 PetscCall(DMPlexComputeBdJacobian_Single_Internal(dm, t, wf, label, numValues, values, fieldI, locX, locX_t, X_tShift, Jac, JacP, coordField, facetIS)); 5743 } 5744 PetscCall(ISDestroy(&facetIS)); 5745 PetscFunctionReturn(PETSC_SUCCESS); 5746 } 5747 5748 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) 5749 { 5750 DM_Plex *mesh = (DM_Plex *)dm->data; 5751 const char *name = "Jacobian"; 5752 DM dmAux = NULL, plex, tdm; 5753 DMEnclosureType encAux; 5754 Vec A, tv; 5755 DMField coordField; 5756 PetscDS prob, probAux = NULL; 5757 PetscSection section, globalSection, sectionAux; 5758 PetscScalar *elemMat, *elemMatP, *elemMatD, *u, *u_t, *a = NULL; 5759 const PetscInt *cells; 5760 PetscInt Nf, fieldI, fieldJ; 5761 PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c; 5762 PetscBool hasJac = PETSC_FALSE, hasPrec = PETSC_FALSE, hasDyn, hasFV = PETSC_FALSE, transform; 5763 5764 PetscFunctionBegin; 5765 PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 5766 if (!cellIS) goto end; 5767 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 5768 PetscCall(ISGetLocalSize(cellIS, &numCells)); 5769 if (cStart >= cEnd) goto end; 5770 PetscCall(DMHasBasisTransform(dm, &transform)); 5771 PetscCall(DMGetBasisTransformDM_Internal(dm, &tdm)); 5772 PetscCall(DMGetBasisTransformVec_Internal(dm, &tv)); 5773 PetscCall(DMGetLocalSection(dm, §ion)); 5774 PetscCall(DMGetGlobalSection(dm, &globalSection)); 5775 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL)); 5776 PetscCall(PetscDSGetNumFields(prob, &Nf)); 5777 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 5778 PetscCall(PetscDSHasJacobian(prob, &hasJac)); 5779 PetscCall(PetscDSHasJacobianPreconditioner(prob, &hasPrec)); 5780 /* user passed in the same matrix, avoid double contributions and 5781 only assemble the Jacobian */ 5782 if (hasJac && Jac == JacP) hasPrec = PETSC_FALSE; 5783 PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn)); 5784 hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE; 5785 PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A)); 5786 if (A) { 5787 PetscCall(VecGetDM(A, &dmAux)); 5788 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 5789 PetscCall(DMConvert(dmAux, DMPLEX, &plex)); 5790 PetscCall(DMGetLocalSection(plex, §ionAux)); 5791 PetscCall(DMGetDS(dmAux, &probAux)); 5792 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 5793 } 5794 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)); 5795 if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a)); 5796 PetscCall(DMGetCoordinateField(dm, &coordField)); 5797 for (c = cStart; c < cEnd; ++c) { 5798 const PetscInt cell = cells ? cells[c] : c; 5799 const PetscInt cind = c - cStart; 5800 PetscScalar *x = NULL, *x_t = NULL; 5801 PetscInt i; 5802 5803 PetscCall(DMPlexVecGetClosure(dm, section, X, cell, NULL, &x)); 5804 for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i]; 5805 PetscCall(DMPlexVecRestoreClosure(dm, section, X, cell, NULL, &x)); 5806 if (X_t) { 5807 PetscCall(DMPlexVecGetClosure(dm, section, X_t, cell, NULL, &x_t)); 5808 for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i]; 5809 PetscCall(DMPlexVecRestoreClosure(dm, section, X_t, cell, NULL, &x_t)); 5810 } 5811 if (dmAux) { 5812 PetscInt subcell; 5813 PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell)); 5814 PetscCall(DMPlexVecGetClosure(plex, sectionAux, A, subcell, NULL, &x)); 5815 for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i]; 5816 PetscCall(DMPlexVecRestoreClosure(plex, sectionAux, A, subcell, NULL, &x)); 5817 } 5818 } 5819 if (hasJac) PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim)); 5820 if (hasPrec) PetscCall(PetscArrayzero(elemMatP, numCells * totDim * totDim)); 5821 if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim)); 5822 for (fieldI = 0; fieldI < Nf; ++fieldI) { 5823 PetscClassId id; 5824 PetscFE fe; 5825 PetscQuadrature qGeom = NULL; 5826 PetscInt Nb; 5827 /* Conforming batches */ 5828 PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize; 5829 /* Remainder */ 5830 PetscInt Nr, offset, Nq; 5831 PetscInt maxDegree; 5832 PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL; 5833 5834 PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe)); 5835 PetscCall(PetscObjectGetClassId((PetscObject)fe, &id)); 5836 if (id == PETSCFV_CLASSID) { 5837 hasFV = PETSC_TRUE; 5838 continue; 5839 } 5840 PetscCall(PetscFEGetDimension(fe, &Nb)); 5841 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 5842 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 5843 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom)); 5844 if (!qGeom) { 5845 PetscCall(PetscFEGetQuadrature(fe, &qGeom)); 5846 PetscCall(PetscObjectReference((PetscObject)qGeom)); 5847 } 5848 PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL)); 5849 PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 5850 blockSize = Nb; 5851 batchSize = numBlocks * blockSize; 5852 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 5853 numChunks = numCells / (numBatches * batchSize); 5854 Ne = numChunks * numBatches * batchSize; 5855 Nr = numCells % (numBatches * batchSize); 5856 offset = numCells - Nr; 5857 PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom)); 5858 PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom)); 5859 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 5860 key.field = fieldI * Nf + fieldJ; 5861 if (hasJac) { 5862 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat)); 5863 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])); 5864 } 5865 if (hasPrec) { 5866 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_PRE, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatP)); 5867 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])); 5868 } 5869 if (hasDyn) { 5870 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD)); 5871 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])); 5872 } 5873 } 5874 PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom)); 5875 PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom)); 5876 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 5877 PetscCall(PetscQuadratureDestroy(&qGeom)); 5878 } 5879 /* Add contribution from X_t */ 5880 if (hasDyn) { 5881 for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c]; 5882 } 5883 if (hasFV) { 5884 PetscClassId id; 5885 PetscFV fv; 5886 PetscInt offsetI, NcI, NbI = 1, fc, f; 5887 5888 for (fieldI = 0; fieldI < Nf; ++fieldI) { 5889 PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fv)); 5890 PetscCall(PetscDSGetFieldOffset(prob, fieldI, &offsetI)); 5891 PetscCall(PetscObjectGetClassId((PetscObject)fv, &id)); 5892 if (id != PETSCFV_CLASSID) continue; 5893 /* Put in the weighted identity */ 5894 PetscCall(PetscFVGetNumComponents(fv, &NcI)); 5895 for (c = cStart; c < cEnd; ++c) { 5896 const PetscInt cind = c - cStart; 5897 const PetscInt eOffset = cind * totDim * totDim; 5898 PetscReal vol; 5899 5900 PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL)); 5901 for (fc = 0; fc < NcI; ++fc) { 5902 for (f = 0; f < NbI; ++f) { 5903 const PetscInt i = offsetI + f * NcI + fc; 5904 if (hasPrec) { 5905 if (hasJac) elemMat[eOffset + i * totDim + i] = vol; 5906 elemMatP[eOffset + i * totDim + i] = vol; 5907 } else { 5908 elemMat[eOffset + i * totDim + i] = vol; 5909 } 5910 } 5911 } 5912 } 5913 } 5914 /* No allocated space for FV stuff, so ignore the zero entries */ 5915 PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_TRUE)); 5916 } 5917 /* Insert values into matrix */ 5918 for (c = cStart; c < cEnd; ++c) { 5919 const PetscInt cell = cells ? cells[c] : c; 5920 const PetscInt cind = c - cStart; 5921 5922 /* Transform to global basis before insertion in Jacobian */ 5923 if (transform) PetscCall(DMPlexBasisTransformPointTensor_Internal(dm, tdm, tv, cell, PETSC_TRUE, totDim, &elemMat[cind * totDim * totDim])); 5924 if (hasPrec) { 5925 if (hasJac) { 5926 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim])); 5927 PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMat[cind * totDim * totDim], ADD_VALUES)); 5928 } 5929 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatP[cind * totDim * totDim])); 5930 PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatP[cind * totDim * totDim], ADD_VALUES)); 5931 } else { 5932 if (hasJac) { 5933 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMat[cind * totDim * totDim])); 5934 PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMat[cind * totDim * totDim], ADD_VALUES)); 5935 } 5936 } 5937 } 5938 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 5939 if (hasFV) PetscCall(MatSetOption(JacP, MAT_IGNORE_ZERO_ENTRIES, PETSC_FALSE)); 5940 PetscCall(PetscFree5(u, u_t, elemMat, elemMatP, elemMatD)); 5941 if (dmAux) { 5942 PetscCall(PetscFree(a)); 5943 PetscCall(DMDestroy(&plex)); 5944 } 5945 /* Compute boundary integrals */ 5946 PetscCall(DMPlexComputeBdJacobian_Internal(dm, X, X_t, t, X_tShift, Jac, JacP, user)); 5947 /* Assemble matrix */ 5948 end: { 5949 PetscBool assOp = hasJac && hasPrec ? PETSC_TRUE : PETSC_FALSE, gassOp; 5950 5951 PetscCall(MPIU_Allreduce(&assOp, &gassOp, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm))); 5952 if (hasJac && hasPrec) { 5953 PetscCall(MatAssemblyBegin(Jac, MAT_FINAL_ASSEMBLY)); 5954 PetscCall(MatAssemblyEnd(Jac, MAT_FINAL_ASSEMBLY)); 5955 } 5956 } 5957 PetscCall(MatAssemblyBegin(JacP, MAT_FINAL_ASSEMBLY)); 5958 PetscCall(MatAssemblyEnd(JacP, MAT_FINAL_ASSEMBLY)); 5959 PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 5960 PetscFunctionReturn(PETSC_SUCCESS); 5961 } 5962 5963 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) 5964 { 5965 DM_Plex *mesh = (DM_Plex *)dm->data; 5966 const char *name = "Hybrid Jacobian"; 5967 DM dmAux[3] = {NULL, NULL, NULL}; 5968 DMLabel ghostLabel = NULL; 5969 DM plex = NULL; 5970 DM plexA = NULL; 5971 PetscDS ds = NULL; 5972 PetscDS dsIn = NULL; 5973 PetscDS dsAux[3] = {NULL, NULL, NULL}; 5974 Vec locA[3] = {NULL, NULL, NULL}; 5975 DM dmScale[3] = {NULL, NULL, NULL}; 5976 PetscDS dsScale[3] = {NULL, NULL, NULL}; 5977 Vec locS[3] = {NULL, NULL, NULL}; 5978 PetscSection section = NULL; 5979 PetscSection sectionAux[3] = {NULL, NULL, NULL}; 5980 DMField coordField = NULL; 5981 PetscScalar *a[3] = {NULL, NULL, NULL}; 5982 PetscScalar *s[3] = {NULL, NULL, NULL}; 5983 PetscScalar *u = NULL, *u_t; 5984 PetscScalar *elemMatNeg, *elemMatPos, *elemMatCoh; 5985 PetscScalar *elemMatNegP, *elemMatPosP, *elemMatCohP; 5986 PetscSection globalSection; 5987 IS chunkIS; 5988 const PetscInt *cells; 5989 PetscInt *faces; 5990 PetscInt cStart, cEnd, numCells; 5991 PetscInt Nf, fieldI, fieldJ, totDim, totDimIn, totDimAux[3], totDimScale[3], numChunks, cellChunkSize, chunk; 5992 PetscInt maxDegree = PETSC_MAX_INT; 5993 PetscQuadrature affineQuad = NULL, *quads = NULL; 5994 PetscFEGeom *affineGeom = NULL, **geoms = NULL; 5995 PetscBool hasBdJac, hasBdPrec; 5996 5997 PetscFunctionBegin; 5998 PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 5999 if (!cellIS) goto end; 6000 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 6001 PetscCall(ISGetLocalSize(cellIS, &numCells)); 6002 if (cStart >= cEnd) goto end; 6003 if ((key[0].label == key[1].label) && (key[0].value == key[1].value) && (key[0].part == key[1].part)) { 6004 const char *name; 6005 PetscCall(PetscObjectGetName((PetscObject)key[0].label, &name)); 6006 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); 6007 } 6008 PetscCall(DMConvert(dm, DMPLEX, &plex)); 6009 PetscCall(DMGetSection(dm, §ion)); 6010 PetscCall(DMGetGlobalSection(dm, &globalSection)); 6011 PetscCall(DMGetLabel(dm, "ghost", &ghostLabel)); 6012 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &ds, &dsIn)); 6013 PetscCall(PetscDSGetNumFields(ds, &Nf)); 6014 PetscCall(PetscDSGetTotalDimension(ds, &totDim)); 6015 PetscCall(PetscDSGetTotalDimension(dsIn, &totDimIn)); 6016 PetscCall(PetscDSHasBdJacobian(ds, &hasBdJac)); 6017 PetscCall(PetscDSHasBdJacobianPreconditioner(ds, &hasBdPrec)); 6018 PetscCall(DMGetAuxiliaryVec(dm, key[2].label, key[2].value, key[2].part, &locA[2])); 6019 if (locA[2]) { 6020 const PetscInt cellStart = cells ? cells[cStart] : cStart; 6021 6022 PetscCall(VecGetDM(locA[2], &dmAux[2])); 6023 PetscCall(DMConvert(dmAux[2], DMPLEX, &plexA)); 6024 PetscCall(DMGetSection(dmAux[2], §ionAux[2])); 6025 PetscCall(DMGetCellDS(dmAux[2], cellStart, &dsAux[2], NULL)); 6026 PetscCall(PetscDSGetTotalDimension(dsAux[2], &totDimAux[2])); 6027 { 6028 const PetscInt *cone; 6029 PetscInt c; 6030 6031 PetscCall(DMPlexGetCone(dm, cellStart, &cone)); 6032 for (c = 0; c < 2; ++c) { 6033 const PetscInt *support; 6034 PetscInt ssize, s; 6035 6036 PetscCall(DMPlexGetSupport(dm, cone[c], &support)); 6037 PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize)); 6038 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); 6039 if (support[0] == cellStart) s = 1; 6040 else if (support[1] == cellStart) s = 0; 6041 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart); 6042 PetscCall(DMGetAuxiliaryVec(dm, key[c].label, key[c].value, key[c].part, &locA[c])); 6043 if (locA[c]) PetscCall(VecGetDM(locA[c], &dmAux[c])); 6044 else dmAux[c] = dmAux[2]; 6045 PetscCall(DMGetCellDS(dmAux[c], support[s], &dsAux[c], NULL)); 6046 PetscCall(PetscDSGetTotalDimension(dsAux[c], &totDimAux[c])); 6047 } 6048 } 6049 } 6050 /* Handle mass matrix scaling 6051 The field in key[2] is the field to be scaled, and the scaling field is the first in the dsScale */ 6052 PetscCall(DMGetAuxiliaryVec(dm, key[2].label, -key[2].value, key[2].part, &locS[2])); 6053 if (locS[2]) { 6054 const PetscInt cellStart = cells ? cells[cStart] : cStart; 6055 PetscInt Nb, Nbs; 6056 6057 PetscCall(VecGetDM(locS[2], &dmScale[2])); 6058 PetscCall(DMGetCellDS(dmScale[2], cells ? cells[cStart] : cStart, &dsScale[2], NULL)); 6059 PetscCall(PetscDSGetTotalDimension(dsScale[2], &totDimScale[2])); 6060 // BRAD: This is not set correctly 6061 key[2].field = 2; 6062 PetscCall(PetscDSGetFieldSize(ds, key[2].field, &Nb)); 6063 PetscCall(PetscDSGetFieldSize(dsScale[2], 0, &Nbs)); 6064 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); 6065 { 6066 const PetscInt *cone; 6067 PetscInt c; 6068 6069 locS[1] = locS[0] = locS[2]; 6070 dmScale[1] = dmScale[0] = dmScale[2]; 6071 PetscCall(DMPlexGetCone(dm, cellStart, &cone)); 6072 for (c = 0; c < 2; ++c) { 6073 const PetscInt *support; 6074 PetscInt ssize, s; 6075 6076 PetscCall(DMPlexGetSupport(dm, cone[c], &support)); 6077 PetscCall(DMPlexGetSupportSize(dm, cone[c], &ssize)); 6078 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); 6079 if (support[0] == cellStart) s = 1; 6080 else if (support[1] == cellStart) s = 0; 6081 else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Face %" PetscInt_FMT " does not have cell %" PetscInt_FMT " in its support", cone[c], cellStart); 6082 PetscCall(DMGetCellDS(dmScale[c], support[s], &dsScale[c], NULL)); 6083 PetscCall(PetscDSGetTotalDimension(dsScale[c], &totDimScale[c])); 6084 } 6085 } 6086 } 6087 /* 2: Setup geometric data */ 6088 PetscCall(DMGetCoordinateField(dm, &coordField)); 6089 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 6090 if (maxDegree > 1) { 6091 PetscInt f; 6092 PetscCall(PetscCalloc2(Nf, &quads, Nf, &geoms)); 6093 for (f = 0; f < Nf; ++f) { 6094 PetscFE fe; 6095 6096 PetscCall(PetscDSGetDiscretization(ds, f, (PetscObject *)&fe)); 6097 if (fe) { 6098 PetscCall(PetscFEGetQuadrature(fe, &quads[f])); 6099 PetscCall(PetscObjectReference((PetscObject)quads[f])); 6100 } 6101 } 6102 } 6103 /* Loop over chunks */ 6104 cellChunkSize = numCells; 6105 numChunks = !numCells ? 0 : PetscCeilReal(((PetscReal)numCells) / cellChunkSize); 6106 PetscCall(PetscCalloc1(2 * cellChunkSize, &faces)); 6107 PetscCall(ISCreateGeneral(PETSC_COMM_SELF, 1 * cellChunkSize, faces, PETSC_USE_POINTER, &chunkIS)); 6108 /* Extract field coefficients */ 6109 /* NOTE This needs the end cap faces to have identical orientations */ 6110 PetscCall(DMPlexGetHybridCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2])); 6111 PetscCall(DMPlexGetHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a)); 6112 PetscCall(DMPlexGetHybridFields(dm, dmScale, dsScale, cellIS, locS, PETSC_TRUE, s)); 6113 PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg)); 6114 PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos)); 6115 PetscCall(DMGetWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh)); 6116 PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP)); 6117 PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP)); 6118 PetscCall(DMGetWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP)); 6119 for (chunk = 0; chunk < numChunks; ++chunk) { 6120 PetscInt cS = cStart + chunk * cellChunkSize, cE = PetscMin(cS + cellChunkSize, cEnd), numCells = cE - cS, c; 6121 6122 if (hasBdJac) { 6123 PetscCall(PetscArrayzero(elemMatNeg, cellChunkSize * totDim * totDim)); 6124 PetscCall(PetscArrayzero(elemMatPos, cellChunkSize * totDim * totDim)); 6125 PetscCall(PetscArrayzero(elemMatCoh, cellChunkSize * totDim * totDim)); 6126 } 6127 if (hasBdPrec) { 6128 PetscCall(PetscArrayzero(elemMatNegP, cellChunkSize * totDim * totDim)); 6129 PetscCall(PetscArrayzero(elemMatPosP, cellChunkSize * totDim * totDim)); 6130 PetscCall(PetscArrayzero(elemMatCohP, cellChunkSize * totDim * totDim)); 6131 } 6132 /* Get faces */ 6133 for (c = cS; c < cE; ++c) { 6134 const PetscInt cell = cells ? cells[c] : c; 6135 const PetscInt *cone; 6136 PetscCall(DMPlexGetCone(plex, cell, &cone)); 6137 faces[(c - cS) * 2 + 0] = cone[0]; 6138 faces[(c - cS) * 2 + 1] = cone[1]; 6139 } 6140 PetscCall(ISGeneralSetIndices(chunkIS, 2 * cellChunkSize, faces, PETSC_USE_POINTER)); 6141 if (maxDegree <= 1) { 6142 if (!affineQuad) PetscCall(DMFieldCreateDefaultQuadrature(coordField, chunkIS, &affineQuad)); 6143 if (affineQuad) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, affineQuad, PETSC_TRUE, &affineGeom)); 6144 } else { 6145 PetscInt f; 6146 for (f = 0; f < Nf; ++f) { 6147 if (quads[f]) PetscCall(DMSNESGetFEGeom(coordField, chunkIS, quads[f], PETSC_TRUE, &geoms[f])); 6148 } 6149 } 6150 6151 for (fieldI = 0; fieldI < Nf; ++fieldI) { 6152 PetscFE feI; 6153 PetscFEGeom *geom = affineGeom ? affineGeom : geoms[fieldI]; 6154 PetscFEGeom *chunkGeom = NULL, *remGeom = NULL; 6155 PetscQuadrature quad = affineQuad ? affineQuad : quads[fieldI]; 6156 PetscInt numChunks, numBatches, batchSize, numBlocks, blockSize, Ne, Nr, offset, Nq, Nb; 6157 PetscBool isCohesiveField; 6158 6159 PetscCall(PetscDSGetDiscretization(ds, fieldI, (PetscObject *)&feI)); 6160 if (!feI) continue; 6161 PetscCall(PetscFEGetTileSizes(feI, NULL, &numBlocks, NULL, &numBatches)); 6162 PetscCall(PetscQuadratureGetData(quad, NULL, NULL, &Nq, NULL, NULL)); 6163 PetscCall(PetscFEGetDimension(feI, &Nb)); 6164 blockSize = Nb; 6165 batchSize = numBlocks * blockSize; 6166 PetscCall(PetscFESetTileSizes(feI, blockSize, numBlocks, batchSize, numBatches)); 6167 numChunks = numCells / (numBatches * batchSize); 6168 Ne = numChunks * numBatches * batchSize; 6169 Nr = numCells % (numBatches * batchSize); 6170 offset = numCells - Nr; 6171 PetscCall(PetscFEGeomGetChunk(geom, 0, offset * 2, &chunkGeom)); 6172 PetscCall(PetscFEGeomGetChunk(geom, offset * 2, numCells * 2, &remGeom)); 6173 PetscCall(PetscDSGetCohesive(ds, fieldI, &isCohesiveField)); 6174 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 6175 PetscFE feJ; 6176 6177 PetscCall(PetscDSGetDiscretization(ds, fieldJ, (PetscObject *)&feJ)); 6178 if (!feJ) continue; 6179 key[0].field = fieldI * Nf + fieldJ; 6180 key[1].field = fieldI * Nf + fieldJ; 6181 key[2].field = fieldI * Nf + fieldJ; 6182 if (hasBdJac) { 6183 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNeg)); 6184 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])); 6185 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPos)); 6186 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])); 6187 } 6188 if (hasBdPrec) { 6189 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[0], 0, Ne, chunkGeom, u, u_t, dsAux[0], a[0], t, X_tShift, elemMatNegP)); 6190 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])); 6191 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[1], 1, Ne, chunkGeom, u, u_t, dsAux[1], a[1], t, X_tShift, elemMatPosP)); 6192 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])); 6193 } 6194 if (hasBdJac) { 6195 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCoh)); 6196 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])); 6197 } 6198 if (hasBdPrec) { 6199 PetscCall(PetscFEIntegrateHybridJacobian(ds, dsIn, PETSCFE_JACOBIAN_PRE, key[2], 2, Ne, chunkGeom, u, u_t, dsAux[2], a[2], t, X_tShift, elemMatCohP)); 6200 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])); 6201 } 6202 } 6203 PetscCall(PetscFEGeomRestoreChunk(geom, offset, numCells, &remGeom)); 6204 PetscCall(PetscFEGeomRestoreChunk(geom, 0, offset, &chunkGeom)); 6205 } 6206 /* Insert values into matrix */ 6207 for (c = cS; c < cE; ++c) { 6208 const PetscInt cell = cells ? cells[c] : c; 6209 const PetscInt cind = c - cS, coff = cind * totDim * totDim; 6210 PetscInt i, j; 6211 6212 /* Scale element values */ 6213 if (locS[0]) { 6214 PetscInt Nb, soff = cind * totDimScale[0], off = 0; 6215 PetscBool cohesive; 6216 6217 for (fieldI = 0; fieldI < Nf; ++fieldI) { 6218 PetscCall(PetscDSGetFieldSize(ds, fieldI, &Nb)); 6219 PetscCall(PetscDSGetCohesive(ds, fieldI, &cohesive)); 6220 6221 if (fieldI == key[2].field) { 6222 PetscCheck(cohesive, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Scaling should not happen for face fields"); 6223 for (i = 0; i < Nb; ++i) { 6224 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]; 6225 if (hasBdPrec) 6226 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]; 6227 } 6228 off += Nb; 6229 } else { 6230 const PetscInt N = cohesive ? Nb : Nb * 2; 6231 6232 for (i = 0; i < N; ++i) { 6233 for (j = 0; j < totDim; ++j) elemMatCoh[coff + (off + i) * totDim + j] += elemMatNeg[coff + (off + i) * totDim + j] + elemMatPos[coff + (off + i) * totDim + j]; 6234 if (hasBdPrec) 6235 for (j = 0; j < totDim; ++j) elemMatCohP[coff + (off + i) * totDim + j] += elemMatNegP[coff + (off + i) * totDim + j] + elemMatPosP[coff + (off + i) * totDim + j]; 6236 } 6237 off += N; 6238 } 6239 } 6240 } else { 6241 for (i = 0; i < totDim * totDim; ++i) elemMatCoh[coff + i] += elemMatNeg[coff + i] + elemMatPos[coff + i]; 6242 if (hasBdPrec) 6243 for (i = 0; i < totDim * totDim; ++i) elemMatCohP[coff + i] += elemMatNegP[coff + i] + elemMatPosP[coff + i]; 6244 } 6245 if (hasBdPrec) { 6246 if (hasBdJac) { 6247 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim])); 6248 PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, Jac, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES)); 6249 } 6250 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCohP[cind * totDim * totDim])); 6251 PetscCall(DMPlexMatSetClosure(plex, section, globalSection, JacP, cell, &elemMatCohP[cind * totDim * totDim], ADD_VALUES)); 6252 } else if (hasBdJac) { 6253 if (mesh->printFEM > 1) PetscCall(DMPrintCellMatrix(cell, name, totDim, totDim, &elemMatCoh[cind * totDim * totDim])); 6254 PetscCall(DMPlexMatSetClosure_Internal(plex, section, globalSection, mesh->useMatClPerm, JacP, cell, &elemMatCoh[cind * totDim * totDim], ADD_VALUES)); 6255 } 6256 } 6257 } 6258 PetscCall(DMPlexRestoreCellFields(dm, cellIS, locX, locX_t, locA[2], &u, &u_t, &a[2])); 6259 PetscCall(DMPlexRestoreHybridFields(dm, dmAux, dsAux, cellIS, locA, PETSC_TRUE, a)); 6260 PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNeg)); 6261 PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPos)); 6262 PetscCall(DMRestoreWorkArray(dm, hasBdJac ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCoh)); 6263 PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatNegP)); 6264 PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatPosP)); 6265 PetscCall(DMRestoreWorkArray(dm, hasBdPrec ? cellChunkSize * totDim * totDim : 0, MPIU_SCALAR, &elemMatCohP)); 6266 PetscCall(PetscFree(faces)); 6267 PetscCall(ISDestroy(&chunkIS)); 6268 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 6269 if (maxDegree <= 1) { 6270 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, affineQuad, PETSC_FALSE, &affineGeom)); 6271 PetscCall(PetscQuadratureDestroy(&affineQuad)); 6272 } else { 6273 PetscInt f; 6274 for (f = 0; f < Nf; ++f) { 6275 if (geoms) PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, quads[f], PETSC_FALSE, &geoms[f])); 6276 if (quads) PetscCall(PetscQuadratureDestroy(&quads[f])); 6277 } 6278 PetscCall(PetscFree2(quads, geoms)); 6279 } 6280 if (dmAux[2]) PetscCall(DMDestroy(&plexA)); 6281 PetscCall(DMDestroy(&plex)); 6282 end: 6283 PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 6284 PetscFunctionReturn(PETSC_SUCCESS); 6285 } 6286 6287 /* 6288 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. 6289 6290 Input Parameters: 6291 + dm - The mesh 6292 . key - The PetscWeakFormKey indicating where integration should happen 6293 . cellIS - The cells to integrate over 6294 . t - The time 6295 . X_tShift - The multiplier for the Jacobian with respect to X_t 6296 . X - Local solution vector 6297 . X_t - Time-derivative of the local solution vector 6298 . Y - Local input vector 6299 - user - the user context 6300 6301 Output Parameter: 6302 . Z - Local output vector 6303 6304 Note: 6305 We form the residual one batch of elements at a time. This allows us to offload work onto an accelerator, 6306 like a GPU, or vectorize on a multicore machine. 6307 */ 6308 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) 6309 { 6310 DM_Plex *mesh = (DM_Plex *)dm->data; 6311 const char *name = "Jacobian"; 6312 DM dmAux = NULL, plex, plexAux = NULL; 6313 DMEnclosureType encAux; 6314 Vec A; 6315 DMField coordField; 6316 PetscDS prob, probAux = NULL; 6317 PetscQuadrature quad; 6318 PetscSection section, globalSection, sectionAux; 6319 PetscScalar *elemMat, *elemMatD, *u, *u_t, *a = NULL, *y, *z; 6320 const PetscInt *cells; 6321 PetscInt Nf, fieldI, fieldJ; 6322 PetscInt totDim, totDimAux = 0, cStart, cEnd, numCells, c; 6323 PetscBool hasDyn; 6324 6325 PetscFunctionBegin; 6326 if (!cellIS) PetscFunctionReturn(PETSC_SUCCESS); 6327 PetscCall(PetscLogEventBegin(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 6328 PetscCall(DMConvert(dm, DMPLEX, &plex)); 6329 PetscCall(ISGetLocalSize(cellIS, &numCells)); 6330 PetscCall(ISGetPointRange(cellIS, &cStart, &cEnd, &cells)); 6331 PetscCall(DMGetLocalSection(dm, §ion)); 6332 PetscCall(DMGetGlobalSection(dm, &globalSection)); 6333 PetscCall(DMGetCellDS(dm, cells ? cells[cStart] : cStart, &prob, NULL)); 6334 PetscCall(PetscDSGetNumFields(prob, &Nf)); 6335 PetscCall(PetscDSGetTotalDimension(prob, &totDim)); 6336 PetscCall(PetscDSHasDynamicJacobian(prob, &hasDyn)); 6337 hasDyn = hasDyn && (X_tShift != 0.0) ? PETSC_TRUE : PETSC_FALSE; 6338 PetscCall(DMGetAuxiliaryVec(dm, key.label, key.value, key.part, &A)); 6339 if (A) { 6340 PetscCall(VecGetDM(A, &dmAux)); 6341 PetscCall(DMGetEnclosureRelation(dmAux, dm, &encAux)); 6342 PetscCall(DMConvert(dmAux, DMPLEX, &plexAux)); 6343 PetscCall(DMGetLocalSection(plexAux, §ionAux)); 6344 PetscCall(DMGetDS(dmAux, &probAux)); 6345 PetscCall(PetscDSGetTotalDimension(probAux, &totDimAux)); 6346 } 6347 PetscCall(VecSet(Z, 0.0)); 6348 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)); 6349 if (dmAux) PetscCall(PetscMalloc1(numCells * totDimAux, &a)); 6350 PetscCall(DMGetCoordinateField(dm, &coordField)); 6351 for (c = cStart; c < cEnd; ++c) { 6352 const PetscInt cell = cells ? cells[c] : c; 6353 const PetscInt cind = c - cStart; 6354 PetscScalar *x = NULL, *x_t = NULL; 6355 PetscInt i; 6356 6357 PetscCall(DMPlexVecGetClosure(plex, section, X, cell, NULL, &x)); 6358 for (i = 0; i < totDim; ++i) u[cind * totDim + i] = x[i]; 6359 PetscCall(DMPlexVecRestoreClosure(plex, section, X, cell, NULL, &x)); 6360 if (X_t) { 6361 PetscCall(DMPlexVecGetClosure(plex, section, X_t, cell, NULL, &x_t)); 6362 for (i = 0; i < totDim; ++i) u_t[cind * totDim + i] = x_t[i]; 6363 PetscCall(DMPlexVecRestoreClosure(plex, section, X_t, cell, NULL, &x_t)); 6364 } 6365 if (dmAux) { 6366 PetscInt subcell; 6367 PetscCall(DMGetEnclosurePoint(dmAux, dm, encAux, cell, &subcell)); 6368 PetscCall(DMPlexVecGetClosure(plexAux, sectionAux, A, subcell, NULL, &x)); 6369 for (i = 0; i < totDimAux; ++i) a[cind * totDimAux + i] = x[i]; 6370 PetscCall(DMPlexVecRestoreClosure(plexAux, sectionAux, A, subcell, NULL, &x)); 6371 } 6372 PetscCall(DMPlexVecGetClosure(plex, section, Y, cell, NULL, &x)); 6373 for (i = 0; i < totDim; ++i) y[cind * totDim + i] = x[i]; 6374 PetscCall(DMPlexVecRestoreClosure(plex, section, Y, cell, NULL, &x)); 6375 } 6376 PetscCall(PetscArrayzero(elemMat, numCells * totDim * totDim)); 6377 if (hasDyn) PetscCall(PetscArrayzero(elemMatD, numCells * totDim * totDim)); 6378 for (fieldI = 0; fieldI < Nf; ++fieldI) { 6379 PetscFE fe; 6380 PetscInt Nb; 6381 /* Conforming batches */ 6382 PetscInt numChunks, numBatches, numBlocks, Ne, blockSize, batchSize; 6383 /* Remainder */ 6384 PetscInt Nr, offset, Nq; 6385 PetscQuadrature qGeom = NULL; 6386 PetscInt maxDegree; 6387 PetscFEGeom *cgeomFEM, *chunkGeom = NULL, *remGeom = NULL; 6388 6389 PetscCall(PetscDSGetDiscretization(prob, fieldI, (PetscObject *)&fe)); 6390 PetscCall(PetscFEGetQuadrature(fe, &quad)); 6391 PetscCall(PetscFEGetDimension(fe, &Nb)); 6392 PetscCall(PetscFEGetTileSizes(fe, NULL, &numBlocks, NULL, &numBatches)); 6393 PetscCall(DMFieldGetDegree(coordField, cellIS, NULL, &maxDegree)); 6394 if (maxDegree <= 1) PetscCall(DMFieldCreateDefaultQuadrature(coordField, cellIS, &qGeom)); 6395 if (!qGeom) { 6396 PetscCall(PetscFEGetQuadrature(fe, &qGeom)); 6397 PetscCall(PetscObjectReference((PetscObject)qGeom)); 6398 } 6399 PetscCall(PetscQuadratureGetData(qGeom, NULL, NULL, &Nq, NULL, NULL)); 6400 PetscCall(DMSNESGetFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 6401 blockSize = Nb; 6402 batchSize = numBlocks * blockSize; 6403 PetscCall(PetscFESetTileSizes(fe, blockSize, numBlocks, batchSize, numBatches)); 6404 numChunks = numCells / (numBatches * batchSize); 6405 Ne = numChunks * numBatches * batchSize; 6406 Nr = numCells % (numBatches * batchSize); 6407 offset = numCells - Nr; 6408 PetscCall(PetscFEGeomGetChunk(cgeomFEM, 0, offset, &chunkGeom)); 6409 PetscCall(PetscFEGeomGetChunk(cgeomFEM, offset, numCells, &remGeom)); 6410 for (fieldJ = 0; fieldJ < Nf; ++fieldJ) { 6411 key.field = fieldI * Nf + fieldJ; 6412 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMat)); 6413 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])); 6414 if (hasDyn) { 6415 PetscCall(PetscFEIntegrateJacobian(prob, PETSCFE_JACOBIAN_DYN, key, Ne, chunkGeom, u, u_t, probAux, a, t, X_tShift, elemMatD)); 6416 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])); 6417 } 6418 } 6419 PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, offset, numCells, &remGeom)); 6420 PetscCall(PetscFEGeomRestoreChunk(cgeomFEM, 0, offset, &chunkGeom)); 6421 PetscCall(DMSNESRestoreFEGeom(coordField, cellIS, qGeom, PETSC_FALSE, &cgeomFEM)); 6422 PetscCall(PetscQuadratureDestroy(&qGeom)); 6423 } 6424 if (hasDyn) { 6425 for (c = 0; c < numCells * totDim * totDim; ++c) elemMat[c] += X_tShift * elemMatD[c]; 6426 } 6427 for (c = cStart; c < cEnd; ++c) { 6428 const PetscInt cell = cells ? cells[c] : c; 6429 const PetscInt cind = c - cStart; 6430 const PetscBLASInt M = totDim, one = 1; 6431 const PetscScalar a = 1.0, b = 0.0; 6432 6433 PetscCallBLAS("BLASgemv", BLASgemv_("N", &M, &M, &a, &elemMat[cind * totDim * totDim], &M, &y[cind * totDim], &one, &b, z, &one)); 6434 if (mesh->printFEM > 1) { 6435 PetscCall(DMPrintCellMatrix(c, name, totDim, totDim, &elemMat[cind * totDim * totDim])); 6436 PetscCall(DMPrintCellVector(c, "Y", totDim, &y[cind * totDim])); 6437 PetscCall(DMPrintCellVector(c, "Z", totDim, z)); 6438 } 6439 PetscCall(DMPlexVecSetClosure(dm, section, Z, cell, z, ADD_VALUES)); 6440 } 6441 PetscCall(PetscFree6(u, u_t, elemMat, elemMatD, y, z)); 6442 if (mesh->printFEM) { 6443 PetscCall(PetscPrintf(PetscObjectComm((PetscObject)Z), "Z:\n")); 6444 PetscCall(VecView(Z, NULL)); 6445 } 6446 PetscCall(ISRestorePointRange(cellIS, &cStart, &cEnd, &cells)); 6447 PetscCall(PetscFree(a)); 6448 PetscCall(DMDestroy(&plexAux)); 6449 PetscCall(DMDestroy(&plex)); 6450 PetscCall(PetscLogEventEnd(DMPLEX_JacobianFEM, dm, 0, 0, 0)); 6451 PetscFunctionReturn(PETSC_SUCCESS); 6452 } 6453