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