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