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