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