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