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