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