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