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