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