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